Pārlūkot izejas kodu

Merge topic 'rpath-on-mac'

dc1d025 OS X: Add test for rpaths on Mac.
8576b3f OS X: Add support for @rpath in export files.
00d71bd Xcode: Add rpath support in Xcode generator.
94e7fef OS X: Add RPATH support for Mac.
Brad King 12 gadi atpakaļ
vecāks
revīzija
3caf565d07

+ 5 - 0
Modules/Platform/Darwin.cmake

@@ -30,6 +30,11 @@ set(CMAKE_SHARED_MODULE_SUFFIX ".so")
 set(CMAKE_MODULE_EXISTS 1)
 set(CMAKE_DL_LIBS "")
 
+# Enable rpath support for 10.5 and greater where it is known to work.
+if("${DARWIN_MAJOR_VERSION}" GREATER 8)
+  set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
+endif()
+
 set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
 set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
 set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")

+ 53 - 7
Source/cmComputeLinkInformation.cxx

@@ -1730,6 +1730,17 @@ void
 cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath,
                                                 cmTarget* target)
 {
+  // Ignore targets on Apple where install_name is not @rpath.
+  // The dependenty library can be found with other means such as
+  // @loader_path or full paths.
+  if(this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    if(!target->HasMacOSXRpath(this->Config))
+      {
+      return;
+      }
+    }
+
   // Libraries with unknown type must be handled using just the file
   // on disk.
   if(target->GetType() == cmTarget::UNKNOWN_LIBRARY)
@@ -1762,25 +1773,60 @@ void
 cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath)
 {
   // Get the name of the library from the file name.
+  bool is_shared_library = false;
   std::string file = cmSystemTools::GetFilenameName(fullPath);
-  if(!this->ExtractSharedLibraryName.find(file.c_str()))
+
+  if(this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    // Check that @rpath is part of the install name.
+    // If it isn't, return.
+    std::string soname;
+    if(!cmSystemTools::GuessLibraryInstallName(fullPath, soname))
+      {
+      return;
+      }
+
+    if(soname.find("@rpath") == std::string::npos)
+      {
+      return;
+      }
+    }
+
+  is_shared_library = this->ExtractSharedLibraryName.find(file.c_str());
+
+  if(!is_shared_library)
     {
     // On some platforms (AIX) a shared library may look static.
     if(this->ArchivesMayBeShared)
       {
-      if(!this->ExtractStaticLibraryName.find(file.c_str()))
+      if(this->ExtractStaticLibraryName.find(file.c_str()))
         {
-        // This is not the name of a shared library or archive.
-        return;
+        // This is the name of a shared library or archive.
+        is_shared_library = true;
         }
       }
-    else
+    }
+
+  // It could be an Apple framework
+  if(!is_shared_library)
+    {
+    if(fullPath.find(".framework") != std::string::npos)
       {
-      // This is not the name of a shared library.
-      return;
+      cmsys::RegularExpression splitFramework;
+      splitFramework.compile("^(.*)/(.*).framework/.*/(.*)$");
+      if(splitFramework.find(fullPath) &&
+        (splitFramework.match(2) == splitFramework.match(3)))
+        {
+        is_shared_library = true;
+        }
       }
     }
 
+  if(!is_shared_library)
+    {
+    return;
+    }
+
   // Include this library in the runtime path ordering.
   this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath);
   if(this->LinkWithRuntimePath)

+ 16 - 0
Source/cmExportBuildFileGenerator.cxx

@@ -211,3 +211,19 @@ cmExportBuildFileGenerator
     << "consider using the APPEND option with multiple separate calls.";
   this->ExportCommand->ErrorMessage = e.str();
 }
+
+std::string
+cmExportBuildFileGenerator::InstallNameDir(cmTarget* target,
+                                           const std::string& config)
+{
+  std::string install_name_dir;
+
+  cmMakefile* mf = target->GetMakefile();
+  if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    install_name_dir =
+      target->GetInstallNameDirForBuildTree(config.c_str());
+    }
+
+  return install_name_dir;
+}

+ 2 - 0
Source/cmExportBuildFileGenerator.h

@@ -61,6 +61,8 @@ protected:
                                  cmTarget* target,
                                  ImportPropertyMap& properties);
 
+  std::string InstallNameDir(cmTarget* target, const std::string& config);
+
   std::vector<cmTarget*> const* Exports;
   cmExportCommand* ExportCommand;
 };

+ 5 - 1
Source/cmExportFileGenerator.cxx

@@ -624,8 +624,12 @@ cmExportFileGenerator
       std::string value;
       if(target->HasSOName(config))
         {
+        if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+          {
+          value = this->InstallNameDir(target, config);
+          }
         prop = "IMPORTED_SONAME";
-        value = target->GetSOName(config);
+        value += target->GetSOName(config);
         }
       else
         {

+ 3 - 0
Source/cmExportFileGenerator.h

@@ -159,6 +159,9 @@ private:
                                     std::vector<std::string> &missingTargets);
 
   virtual void ReplaceInstallPrefix(std::string &input);
+
+  virtual std::string InstallNameDir(cmTarget* target,
+                                     const std::string& config) = 0;
 };
 
 #endif

+ 16 - 0
Source/cmExportInstallFileGenerator.cxx

@@ -491,3 +491,19 @@ cmExportInstallFileGenerator
     }
   cmSystemTools::Error(e.str().c_str());
 }
+
+std::string
+cmExportInstallFileGenerator::InstallNameDir(cmTarget* target,
+                                             const std::string&)
+{
+  std::string install_name_dir;
+
+  cmMakefile* mf = target->GetMakefile();
+  if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    install_name_dir =
+      target->GetInstallNameDirForInstallTree();
+    }
+
+  return install_name_dir;
+}

+ 2 - 0
Source/cmExportInstallFileGenerator.h

@@ -85,6 +85,8 @@ protected:
 
   void ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen);
 
+  std::string InstallNameDir(cmTarget* target, const std::string& config);
+
   cmInstallExportGenerator* IEGen;
 
   std::string ImportPrefix;

+ 15 - 0
Source/cmExportTryCompileFileGenerator.cxx

@@ -112,3 +112,18 @@ cmExportTryCompileFileGenerator::PopulateProperties(cmTarget* target,
       }
     }
 }
+std::string
+cmExportTryCompileFileGenerator::InstallNameDir(cmTarget* target,
+                                                const std::string& config)
+{
+  std::string install_name_dir;
+
+  cmMakefile* mf = target->GetMakefile();
+  if(mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    install_name_dir =
+      target->GetInstallNameDirForBuildTree(config.c_str());
+    }
+
+  return install_name_dir;
+}

+ 2 - 0
Source/cmExportTryCompileFileGenerator.h

@@ -43,6 +43,8 @@ protected:
                           ImportPropertyMap& properties,
                           std::set<cmTarget*> &emitted);
 
+  std::string InstallNameDir(cmTarget* target,
+                             const std::string& config);
 private:
   std::string FindTargets(const char *prop, cmTarget *tgt,
                    std::set<cmTarget*> &emitted);

+ 23 - 0
Source/cmGlobalXCodeGenerator.cxx

@@ -2242,6 +2242,29 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
   buildSettings->AddAttribute("INSTALL_PATH",
                               this->CreateString(install_name_dir.c_str()));
 
+  // Create the LD_RUNPATH_SEARCH_PATHS
+  cmComputeLinkInformation* pcli = target.GetLinkInformation(configName);
+  if(pcli)
+    {
+    std::string search_paths;
+    std::vector<std::string> runtimeDirs;
+    pcli->GetRPath(runtimeDirs, false);
+    for(std::vector<std::string>::const_iterator i = runtimeDirs.begin();
+        i != runtimeDirs.end(); ++i)
+      {
+      if(!search_paths.empty())
+        {
+        search_paths += " ";
+        }
+      search_paths += this->XCodeEscapePath((*i).c_str());
+      }
+    if(!search_paths.empty())
+      {
+      buildSettings->AddAttribute("LD_RUNPATH_SEARCH_PATHS",
+                                  this->CreateString(search_paths.c_str()));
+      }
+    }
+
   buildSettings->AddAttribute("OTHER_LDFLAGS",
                               this->CreateString(extraLinkOptions.c_str()));
   buildSettings->AddAttribute("OTHER_REZFLAGS",

+ 57 - 19
Source/cmInstallTargetGenerator.cxx

@@ -606,6 +606,12 @@ cmInstallTargetGenerator
     return;
     }
 
+  // Skip if on Apple
+  if(this->Target->GetMakefile()->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    return;
+    }
+
   // Get the link information for this target.
   // It can provide the RPATH.
   cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
@@ -645,30 +651,62 @@ cmInstallTargetGenerator
     return;
     }
 
-  // Construct the original rpath string to be replaced.
-  std::string oldRpath = cli->GetRPathString(false);
-
-  // Get the install RPATH from the link information.
-  std::string newRpath = cli->GetChrpathString();
-
-  // Skip the rule if the paths are identical
-  if(oldRpath == newRpath)
+  if(this->Target->GetMakefile()->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
     {
-    return;
-    }
+    // If using install_name_tool, set up the rules to modify the rpaths.
+    std::string installNameTool =
+      this->Target->GetMakefile()->
+      GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
+
+    std::vector<std::string> oldRuntimeDirs, newRuntimeDirs;
+    cli->GetRPath(oldRuntimeDirs, false);
+    cli->GetRPath(newRuntimeDirs, true);
+
+    // Note: These are separate commands to avoid install_name_tool
+    // corruption on 10.6.
+    for(std::vector<std::string>::const_iterator i = oldRuntimeDirs.begin();
+        i != oldRuntimeDirs.end(); ++i)
+      {
+      os << indent << "execute_process(COMMAND " << installNameTool << "\n";
+      os << indent << "  -delete_rpath \"" << *i << "\"\n";
+      os << indent << "  \"" << toDestDirPath << "\")\n";
+      }
 
-  // Write a rule to run chrpath to set the install-tree RPATH
-  if(newRpath.empty())
-    {
-    os << indent << "FILE(RPATH_REMOVE\n"
-       << indent << "     FILE \"" << toDestDirPath << "\")\n";
+    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 \"" << *i << "\"\n";
+      os << indent << "  \"" << toDestDirPath << "\")\n";
+      }
     }
   else
     {
-    os << indent << "FILE(RPATH_CHANGE\n"
-       << indent << "     FILE \"" << toDestDirPath << "\"\n"
-       << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
-       << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
+    // Construct the original rpath string to be replaced.
+    std::string oldRpath = cli->GetRPathString(false);
+
+    // Get the install RPATH from the link information.
+    std::string newRpath = cli->GetChrpathString();
+
+    // Skip the rule if the paths are identical
+    if(oldRpath == newRpath)
+      {
+      return;
+      }
+
+    // Write a rule to run chrpath to set the install-tree RPATH
+    if(newRpath.empty())
+      {
+      os << indent << "FILE(RPATH_REMOVE\n"
+         << indent << "     FILE \"" << toDestDirPath << "\")\n";
+      }
+    else
+      {
+      os << indent << "FILE(RPATH_CHANGE\n"
+         << indent << "     FILE \"" << toDestDirPath << "\"\n"
+         << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
+         << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
+      }
     }
 }
 

+ 41 - 4
Source/cmOrderDirectories.cxx

@@ -36,8 +36,25 @@ public:
     OD(od), GlobalGenerator(od->GlobalGenerator)
     {
     this->FullPath = file;
-    this->Directory = cmSystemTools::GetFilenamePath(file);
-    this->FileName = cmSystemTools::GetFilenameName(file);
+
+    if(file.rfind(".framework") != std::string::npos)
+      {
+      cmsys::RegularExpression splitFramework;
+      splitFramework.compile("^(.*)/(.*).framework/.*/(.*)$");
+      if(splitFramework.find(file) &&
+        (splitFramework.match(2) == splitFramework.match(3)))
+        {
+        this->Directory = splitFramework.match(1);
+        this->FileName =
+          std::string(file.begin() + this->Directory.size() + 1, file.end());
+        }
+      }
+
+    if(this->FileName.empty())
+      {
+      this->Directory = cmSystemTools::GetFilenamePath(file);
+      this->FileName = cmSystemTools::GetFilenameName(file);
+      }
     }
   virtual ~cmOrderDirectoriesConstraint() {}
 
@@ -301,22 +318,42 @@ void cmOrderDirectories::AddRuntimeLibrary(std::string const& fullPath,
   // Add the runtime library at most once.
   if(this->EmmittedConstraintSOName.insert(fullPath).second)
     {
+    std::string soname2 = soname ? soname : "";
     // Implicit link directories need special handling.
     if(!this->ImplicitDirectories.empty())
       {
       std::string dir = cmSystemTools::GetFilenamePath(fullPath);
+
+      if(fullPath.rfind(".framework") != std::string::npos)
+        {
+        cmsys::RegularExpression splitFramework;
+        splitFramework.compile("^(.*)/(.*).framework/(.*)/(.*)$");
+        if(splitFramework.find(fullPath) &&
+          (splitFramework.match(2) == splitFramework.match(4)))
+          {
+          dir = splitFramework.match(1);
+          soname2 = splitFramework.match(2);
+          soname2 += ".framework/";
+          soname2 += splitFramework.match(3);
+          soname2 += "/";
+          soname2 += splitFramework.match(4);
+          }
+        }
+
       if(this->ImplicitDirectories.find(dir) !=
          this->ImplicitDirectories.end())
         {
         this->ImplicitDirEntries.push_back(
-          new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
+          new cmOrderDirectoriesConstraintSOName(this, fullPath,
+                                                 soname2.c_str()));
         return;
         }
       }
 
     // Construct the runtime information entry for this library.
     this->ConstraintEntries.push_back(
-      new cmOrderDirectoriesConstraintSOName(this, fullPath, soname));
+      new cmOrderDirectoriesConstraintSOName(this, fullPath,
+                                             soname2.c_str()));
     }
   else
     {

+ 21 - 0
Source/cmSystemTools.cxx

@@ -2412,6 +2412,27 @@ bool cmSystemTools::GuessLibrarySOName(std::string const& fullPath,
   return false;
 }
 
+//----------------------------------------------------------------------------
+bool cmSystemTools::GuessLibraryInstallName(std::string const& fullPath,
+                                       std::string& soname)
+{
+  std::vector<cmStdString> cmds;
+  cmds.push_back("otool");
+  cmds.push_back("-D");
+  cmds.push_back(fullPath.c_str());
+
+  std::string output;
+  RunSingleCommand(cmds, &output, 0, 0, OUTPUT_NONE);
+
+  std::vector<std::string> strs = cmSystemTools::tokenize(output, "\n");
+  if(strs.size() == 2)
+    {
+    soname = strs[1];
+    return true;
+    }
+  return false;
+}
+
 //----------------------------------------------------------------------------
 #if defined(CMAKE_USE_ELF_PARSER)
 std::string::size_type cmSystemToolsFindRPath(std::string const& have,

+ 4 - 0
Source/cmSystemTools.h

@@ -439,6 +439,10 @@ public:
   static bool GuessLibrarySOName(std::string const& fullPath,
                                  std::string& soname);
 
+  /** Try to guess the install name of a shared library.  */
+  static bool GuessLibraryInstallName(std::string const& fullPath,
+                                 std::string& soname);
+
   /** Try to set the RPATH in an ELF binary.  */
   static bool ChangeRPath(std::string const& file,
                           std::string const& oldRPath,

+ 103 - 2
Source/cmTarget.cxx

@@ -1185,6 +1185,15 @@ void cmTarget::DefineProperties(cmake *cm)
      "If a custom Info.plist is specified by this property it may of course "
      "hard-code all the settings instead of using the target properties.");
 
+  cm->DefineProperty
+    ("MACOSX_RPATH", cmProperty::TARGET,
+     "Whether to use rpaths on Mac OS X.",
+     "When this property is set to true, the directory portion of the"
+     "\"install_name\" field of shared libraries will default to \"@rpath\"."
+     "Runtime paths will also be embedded in binaries using this target."
+     "This property is initialized by the value of the variable "
+     "CMAKE_MACOSX_RPATH if it is set when a target is created.");
+
   cm->DefineProperty
     ("ENABLE_EXPORTS", cmProperty::TARGET,
      "Specify whether an executable exports symbols for loadable modules.",
@@ -1513,6 +1522,8 @@ void cmTarget::SetMakefile(cmMakefile* mf)
   this->SetPropertyDefault("LINK_INTERFACE_LIBRARIES", 0);
   this->SetPropertyDefault("WIN32_EXECUTABLE", 0);
   this->SetPropertyDefault("MACOSX_BUNDLE", 0);
+  this->SetPropertyDefault("MACOSX_RPATH", 0);
+
 
   // Collect the set of configuration types.
   std::vector<std::string> configNames;
@@ -3886,6 +3897,10 @@ std::string cmTarget::GetSOName(const char* config)
       else
         {
         // Use the soname given if any.
+        if(info->SOName.find("@rpath/") == 0)
+          {
+          return info->SOName.substr(6);
+          }
         return info->SOName;
         }
       }
@@ -3907,6 +3922,75 @@ std::string cmTarget::GetSOName(const char* config)
     }
 }
 
+//----------------------------------------------------------------------------
+bool cmTarget::HasMacOSXRpath(const char* config)
+{
+  bool install_name_is_rpath = false;
+  bool macosx_rpath = this->GetPropertyAsBool("MACOSX_RPATH");
+
+  if(!this->IsImportedTarget)
+    {
+    const char* install_name = this->GetProperty("INSTALL_NAME_DIR");
+    bool use_install_name =
+      this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH");
+    if(install_name && use_install_name &&
+       std::string(install_name) == "@rpath")
+      {
+      install_name_is_rpath = true;
+      }
+    }
+  else
+    {
+    // Lookup the imported soname.
+    if(cmTarget::ImportInfo const* info = this->GetImportInfo(config, this))
+      {
+      if(!info->NoSOName && !info->SOName.empty())
+        {
+        if(info->SOName.find("@rpath/") == 0)
+          {
+          install_name_is_rpath = true;
+          }
+        }
+      else
+        {
+        std::string install_name;
+        cmSystemTools::GuessLibraryInstallName(info->Location, install_name);
+        if(install_name.find("@rpath") != std::string::npos)
+          {
+          install_name_is_rpath = true;
+          }
+        }
+      }
+    }
+
+  if(!install_name_is_rpath && !macosx_rpath)
+    {
+    return false;
+    }
+
+  if(!this->Makefile->IsSet("CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG"))
+    {
+    cmOStringStream w;
+    w << "Attempting to use";
+    if(macosx_rpath)
+      {
+      w << " MACOSX_RPATH";
+      }
+    else
+      {
+      w << " @rpath";
+      }
+    w << " without CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG being set.";
+    w << "  This could be because you are using a Mac OS X version";
+    w << " less than 10.5 or because CMake's platform configuration is";
+    w << " corrupt.";
+    cmake* cm = this->Makefile->GetCMakeInstance();
+    cm->IssueMessage(cmake::FATAL_ERROR, w.str(), this->GetBacktrace());
+    }
+
+  return true;
+}
+
 //----------------------------------------------------------------------------
 bool cmTarget::IsImportedSharedLibWithoutSOName(const char* config)
 {
@@ -4547,7 +4631,15 @@ std::string cmTarget::GetInstallNameDirForBuildTree(const char* config)
      !this->Makefile->IsOn("CMAKE_SKIP_RPATH") &&
      !this->GetPropertyAsBool("SKIP_BUILD_RPATH"))
     {
-    std::string dir = this->GetDirectory(config);
+    std::string dir;
+    if(this->GetPropertyAsBool("MACOSX_RPATH"))
+      {
+      dir = "@rpath";
+      }
+    else
+      {
+      dir = this->GetDirectory(config);
+      }
     dir += "/";
     return dir;
     }
@@ -4574,6 +4666,10 @@ std::string cmTarget::GetInstallNameDirForInstallTree()
         dir += "/";
         }
       }
+    if(dir.empty() && this->GetPropertyAsBool("MACOSX_RPATH"))
+      {
+      dir = "@rpath/";
+      }
     return dir;
     }
   else
@@ -5155,7 +5251,6 @@ void cmTarget::GetLanguages(std::set<cmStdString>& languages) const
 //----------------------------------------------------------------------------
 bool cmTarget::IsChrpathUsed(const char* config)
 {
-#if defined(CMAKE_USE_ELF_PARSER)
   // Only certain target types have an rpath.
   if(!(this->GetType() == cmTarget::SHARED_LIBRARY ||
        this->GetType() == cmTarget::MODULE_LIBRARY ||
@@ -5189,6 +5284,12 @@ bool cmTarget::IsChrpathUsed(const char* config)
     return false;
     }
 
+  if(this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME"))
+    {
+    return true;
+    }
+
+#if defined(CMAKE_USE_ELF_PARSER)
   // Enable if the rpath flag uses a separator and the target uses ELF
   // binaries.
   if(const char* ll = this->GetLinkerLanguage(config, this))

+ 9 - 0
Source/cmTarget.h

@@ -367,6 +367,9 @@ public:
   /** Get the soname of the target.  Allowed only for a shared library.  */
   std::string GetSOName(const char* config);
 
+  /** Whether this library has @rpath and platform supports it.  */
+  bool HasMacOSXRpath(const char* config);
+
   /** Test for special case of a third-party shared library that has
       no soname at all.  */
   bool IsImportedSharedLibWithoutSOName(const char* config);
@@ -412,7 +415,13 @@ public:
   /** Return true if builtin chrpath will work for this target */
   bool IsChrpathUsed(const char* config);
 
+  /** Return the install name directory for the target in the
+    * build tree.  For example: "@rpath/", "@loader_path/",
+    * or "/full/path/to/library".  */
   std::string GetInstallNameDirForBuildTree(const char* config);
+
+  /** Return the install name directory for the target in the
+    * install tree.  For example: "@rpath/" or "@loader_path/". */
   std::string GetInstallNameDirForInstallTree();
 
   cmComputeLinkInformation* GetLinkInformation(const char* config,

+ 10 - 0
Tests/CMakeLists.txt

@@ -1233,6 +1233,16 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
     endif()
   endif()
 
+  if(APPLE AND "${DARWIN_MAJOR_VERSION}" GREATER 9)
+    add_test(MacRuntimePath ${CMAKE_CTEST_COMMAND}
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/MacRuntimePath"
+      "${CMake_BINARY_DIR}/Tests/MacRuntimePath"
+      ${build_generator_args}
+      --build-project MacRuntimePath
+      )
+  endif()
+
   add_test(linkorder1 ${CMAKE_CTEST_COMMAND}
     --build-and-test
     "${CMake_SOURCE_DIR}/Tests/LinkLineOrder"

+ 63 - 0
Tests/MacRuntimePath/A/CMakeLists.txt

@@ -0,0 +1,63 @@
+cmake_minimum_required(VERSION 2.8)
+project(MacRuntimePath_A)
+
+# a shared library
+add_library(shared SHARED shared.cpp shared.h)
+set_target_properties(shared PROPERTIES MACOSX_RPATH 1)
+
+# a shared library with custom set @rpath
+add_library(shared2 SHARED shared.cpp shared.h)
+set_target_properties(shared2 PROPERTIES
+  BUILD_WITH_INSTALL_RPATH 1 INSTALL_NAME_DIR "@rpath")
+
+# a framework library
+add_library(framework SHARED framework.cpp framework.h)
+set_target_properties(framework PROPERTIES MACOSX_RPATH 1 FRAMEWORK 1)
+
+# executable to test a shared library dependency with install rpaths
+add_executable(test1 test1.cpp)
+target_link_libraries(test1 shared)
+set_target_properties(test1 PROPERTIES
+  BUILD_WITH_INSTALL_RPATH 1 INSTALL_RPATH "@loader_path/../lib")
+
+# executable to test a framework library dependency with install rpaths
+add_executable(test2 test2.cpp)
+target_link_libraries(test2 framework)
+set_target_properties(test2 PROPERTIES
+  BUILD_WITH_INSTALL_RPATH 1 INSTALL_RPATH "@loader_path/../lib")
+
+# executable to test a framework library dependency with build tree rpaths
+add_executable(test3 test3.cpp)
+target_link_libraries(test3 framework)
+
+# executable to test a framework library dependency with build tree rpaths
+add_executable(test4 test1.cpp)
+target_link_libraries(test4 shared2)
+
+set_target_properties(shared shared2 framework PROPERTIES
+  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib")
+set_target_properties(test1 test2 test3 test4 PROPERTIES
+  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
+foreach(config ${CMAKE_CONFIGURATION_TYPES})
+  string(TOUPPER ${config} CONFIG)
+  set_target_properties(shared shared2 framework PROPERTIES
+    LIBRARY_OUTPUT_DIRECTORY_${CONFIG}
+      "${CMAKE_CURRENT_BINARY_DIR}/${config}/lib")
+  set_target_properties(test1 test2 test3 test4 PROPERTIES
+    RUNTIME_OUTPUT_DIRECTORY_${CONFIG}
+      "${CMAKE_CURRENT_BINARY_DIR}/${config}/bin")
+endforeach()
+
+foreach(test test1 test2 test3 test4)
+  add_custom_target(${test}_run  ALL
+    COMMAND ${test}
+    DEPENDS ${test}
+    )
+endforeach()
+
+export(TARGETS shared shared2 framework FILE "${CMAKE_CURRENT_BINARY_DIR}/exp.cmake")
+
+install(TARGETS shared EXPORT MyExport DESTINATION lib)
+install(TARGETS shared2 EXPORT MyExport DESTINATION lib2)
+install(TARGETS framework EXPORT MyExport DESTINATION lib-fw)
+install(EXPORT MyExport DESTINATION lib FILE exp.cmake)

+ 8 - 0
Tests/MacRuntimePath/A/framework.cpp

@@ -0,0 +1,8 @@
+
+#include "framework.h"
+#include "stdio.h"
+
+void framework()
+{
+  printf("framework\n");
+}

+ 17 - 0
Tests/MacRuntimePath/A/framework.h

@@ -0,0 +1,17 @@
+
+#ifndef framework_h
+#define framework_h
+
+#ifdef WIN32
+# ifdef framework_EXPORTS
+#  define FRAMEWORK_EXPORT __declspec(dllexport)
+# else
+#  define FRAMEWORK_EXPORT __declspec(dllimport)
+# endif
+#else
+# define FRAMEWORK_EXPORT
+#endif
+
+void FRAMEWORK_EXPORT framework();
+
+#endif

+ 8 - 0
Tests/MacRuntimePath/A/shared.cpp

@@ -0,0 +1,8 @@
+
+#include "shared.h"
+#include "stdio.h"
+
+void shared()
+{
+  printf("shared\n");
+}

+ 17 - 0
Tests/MacRuntimePath/A/shared.h

@@ -0,0 +1,17 @@
+
+#ifndef shared_h
+#define shared_h
+
+#ifdef WIN32
+# ifdef shared_EXPORTS
+#  define SHARED_EXPORT __declspec(dllexport)
+# else
+#  define SHARED_EXPORT __declspec(dllimport)
+# endif
+#else
+# define SHARED_EXPORT
+#endif
+
+void SHARED_EXPORT shared();
+
+#endif

+ 8 - 0
Tests/MacRuntimePath/A/test1.cpp

@@ -0,0 +1,8 @@
+
+#include "shared.h"
+
+int main(int, char**)
+{
+  shared();
+  return 0;
+}

+ 8 - 0
Tests/MacRuntimePath/A/test2.cpp

@@ -0,0 +1,8 @@
+
+#include "framework.h"
+
+int main(int, char**)
+{
+  framework();
+  return 0;
+}

+ 8 - 0
Tests/MacRuntimePath/A/test3.cpp

@@ -0,0 +1,8 @@
+
+#include "framework.h"
+
+int main(int, char**)
+{
+  framework();
+  return 0;
+}

+ 17 - 0
Tests/MacRuntimePath/B/CMakeLists.txt

@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 2.8)
+project(MacRuntimePath_B)
+
+include(${MacRuntimePath_B_BINARY_DIR}/../Root/lib/exp.cmake)
+
+add_executable(testb ${MacRuntimePath_B_SOURCE_DIR}/../A/test3.cpp)
+
+# test link with rpath enabled targets
+target_link_libraries(testb shared framework)
+
+# test link with rpath enabled library by filename
+target_link_libraries(testb $<TARGET_LINKER_FILE:shared2> framework)
+
+add_custom_target(testb_run ALL
+  COMMAND testb
+  DEPENDS testb
+  )

+ 72 - 0
Tests/MacRuntimePath/CMakeLists.txt

@@ -0,0 +1,72 @@
+cmake_minimum_required (VERSION 2.8)
+project(MacRuntimePath)
+
+
+# Wipe out the install tree to make sure the exporter works.
+add_custom_command(
+  OUTPUT ${MacRuntimePath_BINARY_DIR}/CleanupProject
+  COMMAND ${CMAKE_COMMAND} -E remove_directory ${MacRuntimePath_BINARY_DIR}/Root
+  )
+add_custom_target(CleanupTarget ALL DEPENDS ${MacRuntimePath_BINARY_DIR}/CleanupProject)
+set_property(
+  SOURCE ${MacRuntimePath_BINARY_DIR}/CleanupProject
+  PROPERTY SYMBOLIC 1
+  )
+
+configure_file(${MacRuntimePath_SOURCE_DIR}/InitialCache.cmake.in
+               ${MacRuntimePath_BINARY_DIR}/InitialCache.cmake @ONLY)
+
+if(CMAKE_CONFIGURATION_TYPES)
+  set(NESTED_CONFIG_TYPE -C "${CMAKE_CFG_INTDIR}")
+else()
+  if(CMAKE_BUILD_TYPE)
+    set(NESTED_CONFIG_TYPE -C "${CMAKE_BUILD_TYPE}")
+  else()
+    set(NESTED_CONFIG_TYPE)
+  endif()
+endif()
+
+# Build and install the exporter.
+add_custom_command(
+  OUTPUT ${MacRuntimePath_BINARY_DIR}/ExportProject
+  COMMAND ${CMAKE_CTEST_COMMAND} ${NESTED_CONFIG_TYPE}
+    --build-and-test
+    ${MacRuntimePath_SOURCE_DIR}/A
+    ${MacRuntimePath_BINARY_DIR}/A
+    --build-noclean
+    --build-project MacRuntimePath_A
+    --build-target install
+    --build-generator ${CMAKE_GENERATOR}
+    --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}"
+    --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+    --build-options -C${MacRuntimePath_BINARY_DIR}/InitialCache.cmake
+  VERBATIM
+  )
+add_custom_target(ExportTarget ALL DEPENDS ${MacRuntimePath_BINARY_DIR}/ExportProject)
+add_dependencies(ExportTarget CleanupTarget)
+set_property(
+  SOURCE ${MacRuntimePath_BINARY_DIR}/ExportProject
+  PROPERTY SYMBOLIC 1
+  )
+
+# Build the importer.
+add_custom_command(
+  OUTPUT ${MacRuntimePath_BINARY_DIR}/ImportProject
+  COMMAND ${CMAKE_CTEST_COMMAND} ${NESTED_CONFIG_TYPE}
+   --build-and-test
+   ${MacRuntimePath_SOURCE_DIR}/B
+   ${MacRuntimePath_BINARY_DIR}/B
+   --build-noclean
+   --build-project MacRuntimePath_B
+   --build-generator ${CMAKE_GENERATOR}
+   --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}"
+   --build-makeprogram ${CMAKE_MAKE_PROGRAM}
+   --build-options -C${MacRuntimePath_BINARY_DIR}/InitialCache.cmake
+  VERBATIM
+  )
+add_custom_target(ImportTarget ALL DEPENDS ${MacRuntimePath_BINARY_DIR}/ImportProject)
+add_dependencies(ImportTarget ExportTarget)
+set_property(
+  SOURCE ${MacRuntimePath_BINARY_DIR}/ImportProject
+  PROPERTY SYMBOLIC 1
+  )

+ 13 - 0
Tests/MacRuntimePath/InitialCache.cmake.in

@@ -0,0 +1,13 @@
+set(CMAKE_C_COMPILER "@CMAKE_C_COMPILER@" CACHE STRING "C Compiler")
+set(CMAKE_C_FLAGS "@CMAKE_C_FLAGS@" CACHE STRING "C Flags")
+set(CMAKE_C_FLAGS_DEBUG "@CMAKE_C_FLAGS_DEBUG@" CACHE STRING "C Flags")
+set(CMAKE_C_FLAGS_RELEASE "@CMAKE_C_FLAGS_RELEASE@" CACHE STRING "C Flags")
+set(CMAKE_C_FLAGS_MINSIZEREL "@CMAKE_C_FLAGS_MINSIZEREL@" CACHE STRING "C Flags")
+set(CMAKE_C_FLAGS_RELWITHDEBINFO "@CMAKE_C_FLAGS_RELWITHDEBINFO@" CACHE STRING "C Flags")
+set(CMAKE_CXX_COMPILER "@CMAKE_CXX_COMPILER@" CACHE STRING "C++ Compiler")
+set(CMAKE_CXX_FLAGS "@CMAKE_CXX_FLAGS@" CACHE STRING "C++ Flags")
+set(CMAKE_CXX_FLAGS_DEBUG "@CMAKE_CXX_FLAGS_DEBUG@" CACHE STRING "C++ Flags")
+set(CMAKE_CXX_FLAGS_RELEASE "@CMAKE_CXX_FLAGS_RELEASE@" CACHE STRING "C++ Flags")
+set(CMAKE_CXX_FLAGS_MINSIZEREL "@CMAKE_CXX_FLAGS_MINSIZEREL@" CACHE STRING "C++ Flags")
+set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "@CMAKE_CXX_FLAGS_RELWITHDEBINFO@" CACHE STRING "C++ Flags")
+set(CMAKE_INSTALL_PREFIX "@MacRuntimePath_BINARY_DIR@/Root" CACHE STRING "Installation Prefix")