CMake error while trying to file(COPY) framework with FOLLOW_SYMLINK_CHAIN

I try to copy Qt frameworks with a file(COPY) command with FOLLOW_SYMLINK_CHAIN. When it tries copying the Resources symlink, I get this error.

CMake Error at /Users/administrator/CMakeProjectTemplate/cmake/CopySymlinkChain.cmake:17 (file):
  file COPY cannot make directory
  "/Users/administrator/CMakeProjectTemplate/Release/install/QtCore.framework/Resources":
  Too many levels of symbolic links.

I go into the Release/instal/QtCore.framework directory and I only see the symlink for the Resources directory, but nothing else. Not sure what is going on here. Would someone give some advice or insight into the problem?

(I would upload a sample project, but I cannot, since I am a new user, so I will just comment as much as I can about a sample project.)

  • Create a framework with similar structure to that of a Qt framework.
administrator@Administrators-MacBook-Pro TestCopyFramework % ls -lR A_Framework.framework
total 0
lrwxr-xr-x  1 administrator  staff   22 Dec 29 20:00 A_Framework -> Versions/A/A_Framework
lrwxr-xr-x  1 administrator  staff   18 Dec 29 19:59 Headers -> Versions/A/Headers
lrwxr-xr-x  1 administrator  staff   20 Dec 29 20:00 Resources -> Versions/A/Resources
drwxr-xr-x  5 administrator  staff  160 Dec 29 20:00 Versions

A_Framework.framework/Versions:
total 0
drwxr-xr-x  6 administrator  staff  192 Dec 29 20:00 A
lrwxr-xr-x  1 administrator  staff    1 Dec 29 19:59 Current -> A

A_Framework.framework/Versions/A:
total 0
-rw-r--r--  1 administrator  staff   0 Dec 29 19:58 A_Framework
drwxr-xr-x  2 administrator  staff  64 Dec 29 19:58 Headers
drwxr-xr-x  2 administrator  staff  64 Dec 29 19:58 Resources

A_Framework.framework/Versions/A/Headers:
total 0

A_Framework.framework/Versions/A/Resources:
total 0
  • Create a script that will copy a source to a destination while copying symlink chains.
# Copy symlink chains

message(STATUS "CMAKE_ARGC: ${CMAKE_ARGC}")
foreach(i RANGE 0 ${CMAKE_ARGC} 1)
  message(STATUS "CMAKE_ARGV${i}: ${CMAKE_ARGV${i}}")
  list(APPEND ARGV ${CMAKE_ARGV${i}})
endforeach()
message(STATUS "ARGV: ${ARGV}")
set(options "")
set(oneValueArgs "DESTINATION")
set(multiValueArgs "SOURCES")
cmake_parse_arguments(COPY "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV})
message(STATUS "COPY_SOURCES: ${COPY_SOURCES}")
message(STATUS "COPY_DESTINATION: ${COPY_DESTINATION}")
file(COPY
  ${COPY_SOURCES}
  DESTINATION ${COPY_DESTINATION}
  FOLLOW_SYMLINK_CHAIN)
  • Run cmake to invoke the script to attempt to copy A_Framework.framework to a destination.
administrator@Administrators-MacBook-Pro TestCopyFramework % cmake -P CopySymlinkChain.cmake -- SOURCES A_Framework.framework DESTINATION A_DestFramework
-- CMAKE_ARGC: 8
-- CMAKE_ARGV0: cmake
-- CMAKE_ARGV1: -P
-- CMAKE_ARGV2: CopySymlinkChain.cmake
-- CMAKE_ARGV3: --
-- CMAKE_ARGV4: SOURCES
-- CMAKE_ARGV5: A_Framework.framework
-- CMAKE_ARGV6: DESTINATION
-- CMAKE_ARGV7: A_DestFramework
-- CMAKE_ARGV8: 
-- ARGV: cmake;-P;CopySymlinkChain.cmake;--;SOURCES;A_Framework.framework;DESTINATION;A_DestFramework
-- COPY_SOURCES: A_Framework.framework
-- COPY_DESTINATION: A_DestFramework
CMake Error at CopySymlinkChain.cmake:17 (file):
  file COPY cannot make directory
  "/Users/administrator/TestCopyFramework/A_DestFramework/A_Framework.framework/Resources":
  Too many levels of symbolic links.

Long story short, it fails.

To get around this limitation, I created my own function called CopyQtFramework



# Function to copy a Qt module framework.
# Parameters:
#   MODULE_NAME is the name of the Qt module.  Framework folder assumed to be Qt${MODULE_NAME}.framework.
#   SOURCE_PATH is the path to the framework.
#   DESTINATION_PATH is the path to which to copy the framework.
function(CopyQtFramework
  MODULE_NAME
  SOURCE_PATH
  DESTINATION_PATH
)
#  message(STATUS "ARGC: ${ARGC}")
#  message(STATUS "ARGV: ${ARGV}")
#  message(STATUS "ARGN: ${ARGN}")
  set(options "")
  set(oneValueArgs "TARGET;SOURCE_PATH;MODULE_NAME;DESTINATION_PATH")
  set(multiValueArgs "")
  cmake_parse_arguments(FRAMEWORK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGV})
#  message(STATUS "FRAMEWORK_SOURCE_PATH: ${FRAMEWORK_SOURCE_PATH}")
#  message(STATUS "FRAMEWORK_MODULE_NAME: ${FRAMEWORK_MODULE_NAME}")
#  message(STATUS "FRAMEWORK_DESTINATION_PATH: ${FRAMEWORK_DESTINATION_PATH}")
  # ================================================================================
  # Qt Frameworks are structured as followed:
  # * Qt<module>.framework
  #   * Headers -> Versions/A/Headers
  #   * Qt<module> -> Versions/A/Qt<module>
  #   * Resources -> Versions/A/Resources
  #   * Versions
  #     * A
  #       * Headers
  #       * Qt<module>
  #       * Resources
  #     * Current -> A
  # ================================================================================
  # Make all directories
  file(MAKE_DIRECTORY
    ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework) # Qt<module>.framework
  file(MAKE_DIRECTORY
    ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions) # Qt<module>.framework/Versions
  file(MAKE_DIRECTORY
    ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A) # Qt<module>.framework/Versions/A
  file(MAKE_DIRECTORY
    ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Headers) # Qt<module>.framework/Versions/A/Headers
  file(MAKE_DIRECTORY
    ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Resources) # Qt<module>.framework/Versions/A/Resources
  # Copy contents of directories
  file(GLOB_RECURSE FRAMEWORK_HEADERS_FILES ${FRAMEWORK_SOURCE_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Headers/*)
  file(COPY
    ${FRAMEWORK_HEADERS_FILES}
    DESTINATION ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Headers)
  file(GLOB_RECURSE FRAMEWORK_RESOURCES_FILES ${FRAMEWORK_SOURCE_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Resources/*)
  file(COPY
    ${FRAMEWORK_RESOURCES_FILES}
    DESTINATION ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Resources)
  file(COPY
    ${FRAMEWORK_SOURCE_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Qt${FRAMEWORK_MODULE_NAME}
    DESTINATION ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A)
  # Create the symlinks
  file(CREATE_LINK ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Headers ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Headers SYMBOLIC)
  file(CREATE_LINK ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Resources ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Resources SYMBOLIC)
  file(CREATE_LINK ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/Current SYMBOLIC)
  file(CREATE_LINK ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Versions/A/Qt${FRAMEWORK_MODULE_NAME} ${FRAMEWORK_DESTINATION_PATH}/Qt${FRAMEWORK_MODULE_NAME}.framework/Qt${FRAMEWORK_MODULE_NAME} SYMBOLIC)
endfunction()