Преглед изворни кода

Merge topic 'fix-OSX-bundle-rpaths-and-Qt5'

631fadea Help: Add notes for topic 'fix-OSX-bundle-rpaths-and-Qt5'
50e261dd OSX: Warn when attempting to change runtime paths on OS X 10.5
9b98fd52 cmake-gui: Make sure we bundle Qt5 Cocoa platform plugin
83a06bb4 BundleUtilities: Framework codesign Resources/Info.plist & Current
f7df82ac BundleUtilities: Resolve & replace @rpath placeholders
14bc686f GetPrerequisites: Make sure dyld placeholders are prefixes
6c313797 BundleUtilities: Use find on UNIX for fast executable lookup
Brad King пре 11 година
родитељ
комит
26bffa6e74

+ 6 - 0
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.

+ 124 - 20
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)

+ 17 - 12
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()
 

+ 27 - 1
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()
 

+ 53 - 27
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";
+          }
         }
       }
     }

+ 2 - 1
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