diff options
-rw-r--r-- | Help/release/dev/fix-OSX-bundle-rpaths-and-Qt5.rst | 6 | ||||
-rw-r--r-- | Modules/BundleUtilities.cmake | 144 | ||||
-rw-r--r-- | Modules/GetPrerequisites.cmake | 29 | ||||
-rw-r--r-- | Source/QtDialog/CMakeLists.txt | 28 | ||||
-rw-r--r-- | Source/cmInstallTargetGenerator.cxx | 80 | ||||
-rw-r--r-- | Tests/BundleUtilities/CMakeLists.txt | 3 |
6 files changed, 229 insertions, 61 deletions
diff --git a/Help/release/dev/fix-OSX-bundle-rpaths-and-Qt5.rst b/Help/release/dev/fix-OSX-bundle-rpaths-and-Qt5.rst new file mode 100644 index 0000000..c8246c5 --- /dev/null +++ b/Help/release/dev/fix-OSX-bundle-rpaths-and-Qt5.rst @@ -0,0 +1,6 @@ +fix-OSX-bundle-rpaths-and-Qt5 +----------------------------- + +* The :module:`BundleUtilities` module learned to resolve and replace + ``@rpath`` placeholders on OS X to correctly bundle applications + using them. diff --git a/Modules/BundleUtilities.cmake b/Modules/BundleUtilities.cmake index 0046c97..445c719 100644 --- a/Modules/BundleUtilities.cmake +++ b/Modules/BundleUtilities.cmake @@ -19,6 +19,7 @@ # get_bundle_and_executable # get_bundle_all_executables # get_item_key +# get_item_rpaths # clear_bundle_keys # set_bundle_key_values # get_bundle_keys @@ -124,7 +125,7 @@ # :: # # SET_BUNDLE_KEY_VALUES(<keys_var> <context> <item> <exepath> <dirs> -# <copyflag>) +# <copyflag> [<rpaths>]) # # Add a key to the list (if necessary) for the given item. If added, # also set all the variables associated with that key. @@ -378,7 +379,25 @@ endfunction() function(get_bundle_all_executables bundle exes_var) set(exes "") - file(GLOB_RECURSE file_list "${bundle}/*") + if(UNIX) + find_program(find_cmd "find") + mark_as_advanced(find_cmd) + endif() + + # find command is much quicker than checking every file one by one on Unix + # which can take long time for large bundles, and since anyway we expect + # executable to have execute flag set we can narrow the list much quicker. + if(find_cmd) + execute_process(COMMAND "${find_cmd}" "${bundle}" + -type f \( -perm -0100 -o -perm -0010 -o -perm -0001 \) + OUTPUT_VARIABLE file_list + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REPLACE "\n" ";" file_list "${file_list}") + else() + file(GLOB_RECURSE file_list "${bundle}/*") + endif() + foreach(f ${file_list}) is_file_executable("${f}" is_executable) if(is_executable) @@ -390,6 +409,29 @@ function(get_bundle_all_executables bundle exes_var) endfunction() +function(get_item_rpaths item rpaths_var) + if(APPLE) + find_program(otool_cmd "otool") + mark_as_advanced(otool_cmd) + endif() + + if(otool_cmd) + execute_process( + COMMAND "${otool_cmd}" -l "${item}" + OUTPUT_VARIABLE load_cmds_ov + ) + string(REGEX REPLACE "[^\n]+cmd LC_RPATH\n[^\n]+\n[^\n]+path ([^\n]+) \\(offset[^\n]+\n" "rpath \\1\n" load_cmds_ov "${load_cmds_ov}") + string(REGEX MATCHALL "rpath [^\n]+" load_cmds_ov "${load_cmds_ov}") + string(REGEX REPLACE "rpath " "" load_cmds_ov "${load_cmds_ov}") + if(load_cmds_ov) + gp_append_unique(${rpaths_var} "${load_cmds_ov}") + endif() + endif() + + set(${rpaths_var} ${${rpaths_var}} PARENT_SCOPE) +endfunction() + + function(get_item_key item key_var) get_filename_component(item_name "${item}" NAME) if(WIN32) @@ -408,12 +450,14 @@ function(clear_bundle_keys keys_var) set(${key}_EMBEDDED_ITEM PARENT_SCOPE) set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE) set(${key}_COPYFLAG PARENT_SCOPE) + set(${key}_RPATHS PARENT_SCOPE) endforeach() set(${keys_var} PARENT_SCOPE) endfunction() function(set_bundle_key_values keys_var context item exepath dirs copyflag) + set(rpaths "${ARGV6}") get_filename_component(item_name "${item}" NAME) get_item_key("${item}" key) @@ -423,10 +467,12 @@ function(set_bundle_key_values keys_var context item exepath dirs copyflag) list(LENGTH ${keys_var} length_after) if(NOT length_before EQUAL length_after) - gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item) + gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}") gp_item_default_embedded_path("${item}" default_embedded_path) + get_item_rpaths("${resolved_item}" item_rpaths) + if(item MATCHES "[^/]+\\.framework/") # For frameworks, construct the name under the embedded path from the # opening "${item_name}.framework/" to the closing "/${item_name}": @@ -462,6 +508,8 @@ function(set_bundle_key_values keys_var context item exepath dirs copyflag) set(${key}_EMBEDDED_ITEM "${embedded_item}" PARENT_SCOPE) set(${key}_RESOLVED_EMBEDDED_ITEM "${resolved_embedded_item}" PARENT_SCOPE) set(${key}_COPYFLAG "${copyflag}" PARENT_SCOPE) + set(${key}_RPATHS "${item_rpaths}" PARENT_SCOPE) + set(${key}_RDEP_RPATHS "${rpaths}" PARENT_SCOPE) else() #message("warning: item key '${key}' already in the list, subsequent references assumed identical to first") endif() @@ -482,18 +530,27 @@ function(get_bundle_keys app libs dirs keys_var) # get_bundle_all_executables("${bundle}" exes) + # Set keys for main executable first: + # + set_bundle_key_values(${keys_var} "${executable}" "${executable}" "${exepath}" "${dirs}" 0) + + # Get rpaths specified by main executable: + # + get_item_key("${executable}" executable_key) + set(main_rpaths "${${executable_key}_RPATHS}") + # For each extra lib, accumulate a key as well and then also accumulate # any of its prerequisites. (Extra libs are typically dynamically loaded # plugins: libraries that are prerequisites for full runtime functionality # but that do not show up in otool -L output...) # foreach(lib ${libs}) - set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0) + set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0 "${main_rpaths}") set(prereqs "") - get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}") + get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}" "${main_rpaths}") foreach(pr ${prereqs}) - set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1) + set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1 "${main_rpaths}") endforeach() endforeach() @@ -502,16 +559,27 @@ function(get_bundle_keys app libs dirs keys_var) # binaries in the bundle have been analyzed. # foreach(exe ${exes}) - # Add the exe itself to the keys: + # Main executable is scanned first above: # - set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0) + if(NOT "${exe}" STREQUAL "${executable}") + # Add the exe itself to the keys: + # + set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0 "${main_rpaths}") + + # Get rpaths specified by executable: + # + get_item_key("${exe}" exe_key) + set(exe_rpaths "${main_rpaths}" "${${exe_key}_RPATHS}") + else() + set(exe_rpaths "${main_rpaths}") + endif() # Add each prerequisite to the keys: # set(prereqs "") - get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}") + get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}" "${exe_rpaths}") foreach(pr ${prereqs}) - set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1) + set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1 "${exe_rpaths}") endforeach() endforeach() @@ -525,6 +593,8 @@ function(get_bundle_keys app libs dirs keys_var) set(${key}_EMBEDDED_ITEM "${${key}_EMBEDDED_ITEM}" PARENT_SCOPE) set(${key}_RESOLVED_EMBEDDED_ITEM "${${key}_RESOLVED_EMBEDDED_ITEM}" PARENT_SCOPE) set(${key}_COPYFLAG "${${key}_COPYFLAG}" PARENT_SCOPE) + set(${key}_RPATHS "${${key}_RPATHS}" PARENT_SCOPE) + set(${key}_RDEP_RPATHS "${${key}_RDEP_RPATHS}" PARENT_SCOPE) endforeach() endif() endfunction() @@ -580,11 +650,30 @@ function(copy_resolved_framework_into_bundle resolved_item resolved_embedded_ite execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}") # Plus Resources, if they exist: - string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}") - string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}") + string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}") + string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}") if(EXISTS "${resolved_resources}") #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_resources}' '${resolved_embedded_resources}'") execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_resources}" "${resolved_embedded_resources}") + else() + # Otherwise try at least copy Contents/Info.plist to Resources/Info.plist, if it exists: + string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Contents/Info.plist" resolved_info_plist "${resolved_item}") + string(REGEX REPLACE "^(.*)/[^/]+$" "\\1/Resources/Info.plist" resolved_embedded_info_plist "${resolved_embedded_item}") + if(EXISTS "${resolved_info_plist}") + #message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_info_plist}' '${resolved_embedded_info_plist}'") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_info_plist}" "${resolved_embedded_info_plist}") + endif() + endif() + + # Check if framework is versioned and fix it layout + string(REGEX REPLACE "^.*/([^/]+)/[^/]+$" "\\1" resolved_embedded_version "${resolved_embedded_item}") + string(REGEX REPLACE "^(.*)/[^/]+/[^/]+$" "\\1" resolved_embedded_versions "${resolved_embedded_item}") + string(REGEX REPLACE "^.*/([^/]+)/[^/]+/[^/]+$" "\\1" resolved_embedded_versions_basename "${resolved_embedded_item}") + if(resolved_embedded_versions_basename STREQUAL "Versions") + # Ensure Current symlink points to the framework version + if(NOT EXISTS "${resolved_embedded_versions}/Current") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink "${resolved_embedded_version}" "${resolved_embedded_versions}/Current") + endif() endif() endif() if(UNIX AND NOT APPLE) @@ -630,8 +719,10 @@ function(fixup_bundle_item resolved_embedded_item exepath dirs) message(FATAL_ERROR "cannot fixup an item that is not in the bundle...") endif() + set(rpaths "${${ikey}_RPATHS}" "${${ikey}_RDEP_RPATHS}") + set(prereqs "") - get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}") + get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}" "${rpaths}") set(changes "") @@ -651,12 +742,28 @@ function(fixup_bundle_item resolved_embedded_item exepath dirs) execute_process(COMMAND chmod u+w "${resolved_embedded_item}") endif() + # Only if install_name_tool supports -delete_rpath: + # + execute_process(COMMAND install_name_tool + OUTPUT_VARIABLE install_name_tool_usage + ERROR_VARIABLE install_name_tool_usage + ) + if(install_name_tool_usage MATCHES ".*-delete_rpath.*") + foreach(rpath ${${ikey}_RPATHS}) + set(changes ${changes} -delete_rpath "${rpath}") + endforeach() + endif() + + if(${ikey}_EMBEDDED_ITEM) + set(changes ${changes} -id "${${ikey}_EMBEDDED_ITEM}") + endif() + # Change this item's id and all of its references in one call # to install_name_tool: # - execute_process(COMMAND install_name_tool - ${changes} -id "${${ikey}_EMBEDDED_ITEM}" "${resolved_embedded_item}" - ) + if(changes) + execute_process(COMMAND install_name_tool ${changes} "${resolved_embedded_item}") + endif() endfunction() @@ -747,10 +854,8 @@ function(verify_bundle_prerequisites bundle result_var info_var) get_bundle_main_executable("${bundle}" main_bundle_exe) - file(GLOB_RECURSE file_list "${bundle}/*") + get_bundle_all_executables("${bundle}" file_list) foreach(f ${file_list}) - is_file_executable("${f}" is_executable) - if(is_executable) get_filename_component(exepath "${f}" PATH) math(EXPR count "${count} + 1") @@ -789,7 +894,6 @@ function(verify_bundle_prerequisites bundle result_var info_var) set(result 0) set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n") endif() - endif() endforeach() if(result) diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake index 05c2edb..9963517 100644 --- a/Modules/GetPrerequisites.cmake +++ b/Modules/GetPrerequisites.cmake @@ -41,7 +41,7 @@ # :: # # GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse> -# <exepath> <dirs>) +# <exepath> <dirs> [<rpaths>]) # # Get the list of shared library files required by <target>. The list # in the variable named <prerequisites_var> should be empty on first @@ -113,7 +113,8 @@ # # :: # -# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>) +# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var> +# [<rpaths>]) # # Resolve an item into an existing full path file. # @@ -122,7 +123,8 @@ # # :: # -# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>) +# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var> +# [<rpaths>]) # # Return the type of <file> with respect to <original_file>. String # describing type of prerequisite is returned in variable named @@ -321,6 +323,7 @@ endfunction() function(gp_resolve_item context item exepath dirs resolved_item_var) set(resolved 0) set(resolved_item "${item}") + set(rpaths "${ARGV5}") # Is it already resolved? # @@ -329,7 +332,7 @@ function(gp_resolve_item context item exepath dirs resolved_item_var) endif() if(NOT resolved) - if(item MATCHES "@executable_path") + if(item MATCHES "^@executable_path") # # @executable_path references are assumed relative to exepath # @@ -347,7 +350,7 @@ function(gp_resolve_item context item exepath dirs resolved_item_var) endif() if(NOT resolved) - if(item MATCHES "@loader_path") + if(item MATCHES "^@loader_path") # # @loader_path references are assumed relative to the # PATH of the given "context" (presumably another library) @@ -367,7 +370,7 @@ function(gp_resolve_item context item exepath dirs resolved_item_var) endif() if(NOT resolved) - if(item MATCHES "@rpath") + if(item MATCHES "^@rpath") # # @rpath references are relative to the paths built into the binaries with -rpath # We handle this case like we do for other Unixes @@ -375,9 +378,9 @@ function(gp_resolve_item context item exepath dirs resolved_item_var) string(REPLACE "@rpath/" "" norpath_item "${item}") set(ri "ri-NOTFOUND") - find_file(ri "${norpath_item}" ${exepath} ${dirs} NO_DEFAULT_PATH) + find_file(ri "${norpath_item}" ${exepath} ${dirs} ${rpaths} NO_DEFAULT_PATH) if(ri) - #message(STATUS "info: 'find_file' in exepath/dirs (${ri})") + #message(STATUS "info: 'find_file' in exepath/dirs/rpaths (${ri})") set(resolved 1) set(resolved_item "${ri}") set(ri "ri-NOTFOUND") @@ -471,6 +474,7 @@ endfunction() function(gp_resolved_file_type original_file file exepath dirs type_var) + set(rpaths "${ARGV5}") #message(STATUS "**") if(NOT IS_ABSOLUTE "${original_file}") @@ -489,7 +493,7 @@ function(gp_resolved_file_type original_file file exepath dirs type_var) if(NOT is_embedded) if(NOT IS_ABSOLUTE "${file}") - gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file) + gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file "${rpaths}") endif() string(TOLOWER "${original_file}" original_lower) @@ -612,6 +616,7 @@ endfunction() function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs) set(verbose 0) set(eol_char "E") + set(rpaths "${ARGV6}") if(NOT IS_ABSOLUTE "${target}") message("warning: target '${target}' is not absolute...") @@ -834,7 +839,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa if(add_item AND ${exclude_system}) set(type "") - gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type) + gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type "${rpaths}") if("${type}" STREQUAL "system") set(add_item 0) @@ -855,7 +860,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa # that the analysis tools can simply accept it as input. # if(NOT list_length_before_append EQUAL list_length_after_append) - gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item) + gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}") set(unseen_prereqs ${unseen_prereqs} "${resolved_item}") endif() endif() @@ -874,7 +879,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa if(${recurse}) set(more_inputs ${unseen_prereqs}) foreach(input ${more_inputs}) - get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}") + get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}" "${rpaths}") endforeach() endif() diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt index 8da88c1..03c2fb4 100644 --- a/Source/QtDialog/CMakeLists.txt +++ b/Source/QtDialog/CMakeLists.txt @@ -35,6 +35,32 @@ if (Qt5Widgets_FOUND) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") + # We need to install Cocoa platform plugin and add qt.conf for Qt5 on Mac. + # FIXME: This should be part of Qt5 CMake scripts, but unfortunatelly + # Qt5 Mac support is missing there. + if(APPLE) + macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var) + get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION) + if(EXISTS "${_qt_plugin_path}") + get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME) + get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH) + get_filename_component(_qt_plugin_type "${_qt_plugin_type}" NAME) + set(_qt_plugin_dest "${CMAKE_INSTALL_PREFIX}/PlugIns/${_qt_plugin_type}") + install(FILES "${_qt_plugin_path}" + DESTINATION "${_qt_plugin_dest}") + set(${_qt_plugins_var} + "${${_qt_plugins_var}};${_qt_plugin_dest}/${_qt_plugin_file}") + else() + message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found") + endif() + endmacro() + install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + "[Paths]\nPlugins = PlugIns\n") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources") + endif() + if(WIN32 AND TARGET Qt5::Core) get_property(_Qt5_Core_LOCATION TARGET Qt5::Core PROPERTY LOCATION) get_filename_component(Qt_BIN_DIR "${_Qt5_Core_LOCATION}" PATH) @@ -168,7 +194,7 @@ if(APPLE OR WIN32) install(CODE " include(\"${CMake_SOURCE_DIR}/Modules/BundleUtilities.cmake\") set(BU_CHMOD_BUNDLE_ITEMS ON) - fixup_bundle(\"${fixup_exe}\" \"\" \"${QT_LIBRARY_DIR};${QT_BINARY_DIR}\") + fixup_bundle(\"${fixup_exe}\" \"${QT_PLUGINS}\" \"${QT_LIBRARY_DIR};${QT_BINARY_DIR}\") ") endif() diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 8a1c53e..d689c89 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -677,46 +677,72 @@ cmInstallTargetGenerator return; } - if(this->Target->GetMakefile()->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) + cmMakefile* mf = this->Target->GetMakefile(); + + if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) { // If using install_name_tool, set up the rules to modify the rpaths. std::string installNameTool = - this->Target->GetMakefile()-> - GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL"); + mf->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL"); std::vector<std::string> oldRuntimeDirs, newRuntimeDirs; cli->GetRPath(oldRuntimeDirs, false); cli->GetRPath(newRuntimeDirs, true); - // Note: These paths are kept unique to avoid install_name_tool corruption. - std::set<std::string> runpaths; - for(std::vector<std::string>::const_iterator i = oldRuntimeDirs.begin(); - i != oldRuntimeDirs.end(); ++i) - { - std::string runpath = this->Target->GetMakefile()->GetLocalGenerator()-> - GetGlobalGenerator()->ExpandCFGIntDir(*i, config); + std::string darwin_major_version_s = + mf->GetSafeDefinition("DARWIN_MAJOR_VERSION"); - if(runpaths.find(runpath) == runpaths.end()) - { - runpaths.insert(runpath); - os << indent << "execute_process(COMMAND " << installNameTool << "\n"; - os << indent << " -delete_rpath \"" << runpath << "\"\n"; - os << indent << " \"" << toDestDirPath << "\")\n"; - } + std::stringstream ss(darwin_major_version_s); + int darwin_major_version; + ss >> darwin_major_version; + if(!ss.fail() && darwin_major_version <= 9 && + (!oldRuntimeDirs.empty() || !newRuntimeDirs.empty()) + ) + { + cmOStringStream msg; + msg << "WARNING: Target \"" << this->Target->GetName() + << "\" has runtime paths which cannot be changed during install. " + << "To change runtime paths, OS X version 10.6 or newer is required. " + << "Therefore, runtime paths will not be changed when installing. " + << "CMAKE_BUILD_WITH_INSTALL_RPATH may be used to work around" + " this limitation."; + mf->IssueMessage(cmake::WARNING, msg.str()); } - - runpaths.clear(); - for(std::vector<std::string>::const_iterator i = newRuntimeDirs.begin(); - i != newRuntimeDirs.end(); ++i) + else { - std::string runpath = this->Target->GetMakefile()->GetLocalGenerator()-> - GetGlobalGenerator()->ExpandCFGIntDir(*i, config); + // Note: These paths are kept unique to avoid + // install_name_tool corruption. + std::set<std::string> runpaths; + for(std::vector<std::string>::const_iterator i = oldRuntimeDirs.begin(); + i != oldRuntimeDirs.end(); ++i) + { + std::string runpath = + mf->GetLocalGenerator()-> + GetGlobalGenerator()->ExpandCFGIntDir(*i, config); + + if(runpaths.find(runpath) == runpaths.end()) + { + runpaths.insert(runpath); + os << indent << "execute_process(COMMAND " << installNameTool <<"\n"; + os << indent << " -delete_rpath \"" << runpath << "\"\n"; + os << indent << " \"" << toDestDirPath << "\")\n"; + } + } - if(runpaths.find(runpath) == runpaths.end()) + runpaths.clear(); + for(std::vector<std::string>::const_iterator i = newRuntimeDirs.begin(); + i != newRuntimeDirs.end(); ++i) { - os << indent << "execute_process(COMMAND " << installNameTool << "\n"; - os << indent << " -add_rpath \"" << runpath << "\"\n"; - os << indent << " \"" << toDestDirPath << "\")\n"; + std::string runpath = + mf->GetLocalGenerator()-> + GetGlobalGenerator()->ExpandCFGIntDir(*i, config); + + if(runpaths.find(runpath) == runpaths.end()) + { + os << indent << "execute_process(COMMAND " << installNameTool <<"\n"; + os << indent << " -add_rpath \"" << runpath << "\"\n"; + os << indent << " \"" << toDestDirPath << "\")\n"; + } } } } diff --git a/Tests/BundleUtilities/CMakeLists.txt b/Tests/BundleUtilities/CMakeLists.txt index 3a1cf55..69ef535 100644 --- a/Tests/BundleUtilities/CMakeLists.txt +++ b/Tests/BundleUtilities/CMakeLists.txt @@ -109,7 +109,8 @@ if(APPLE AND NOT CMAKE_SYSTEM_VERSION VERSION_LESS 9.0) target_link_libraries(testbundleutils3 shared-3 framework-3 ${CMAKE_DL_LIBS}) set_target_properties(testbundleutils3 module3 PROPERTIES - LINK_FLAGS "-Wl,-rpath,@loader_path/") + LINK_FLAGS "-Wl,-rpath,@loader_path/" + BUILD_WITH_INSTALL_RPATH 1) # add custom target to install and test the app add_custom_target(testbundleutils3_test ALL |