Browse Source

BUG: Correct Mac OS X framework behavior

  - Place the built library in foo.framework/Versions/A/foo
  - Do not create unused content symlinks (like PrivateHeaders)
  - Do not use VERSION/SOVERSION properties for frameworks
  - Make cmTarget::GetDirectory return by value
  - Remove the foo.framework part from cmTarget::GetDirectory
  - Correct install_name construction and conversion on install
  - Fix MACOSX_PACKAGE_LOCATION under Xcode to use the
    Versions/<version> directory for frameworks
  - Update the Framework test to try these things
Brad King 17 years ago
parent
commit
67834f2d53

+ 3 - 13
Source/cmComputeLinkInformation.cxx

@@ -611,18 +611,8 @@ void cmComputeLinkInformation::AddItem(std::string const& item, cmTarget* tgt)
       std::string lib = tgt->GetFullPath(config, implib, true);
       std::string lib = tgt->GetFullPath(config, implib, true);
       this->Depends.push_back(lib);
       this->Depends.push_back(lib);
 
 
-      if(tgt->IsFrameworkOnApple())
-        {
-        // Frameworks on OS X need only the framework directory to
-        // link.
-        std::string fw = tgt->GetDirectory(config, implib);
-        this->AddFrameworkItem(fw);
-        }
-      else
-        {
-        this->AddTargetItem(lib, tgt);
-        this->AddLibraryRuntimeInfo(lib, tgt);
-        }
+      this->AddTargetItem(lib, tgt);
+      this->AddLibraryRuntimeInfo(lib, tgt);
       }
       }
     }
     }
   else
   else
@@ -1023,7 +1013,7 @@ void cmComputeLinkInformation::AddTargetItem(std::string const& item,
 
 
   // For compatibility with CMake 2.4 include the item's directory in
   // For compatibility with CMake 2.4 include the item's directory in
   // the linker search path.
   // the linker search path.
-  if(this->OldLinkDirMode &&
+  if(this->OldLinkDirMode && !target->IsFrameworkOnApple() &&
      this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) ==
      this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) ==
      this->OldLinkDirMask.end())
      this->OldLinkDirMask.end())
     {
     {

+ 13 - 48
Source/cmGlobalXCodeGenerator.cxx

@@ -803,8 +803,14 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
         copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
         copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
           this->CreateString("6"));
           this->CreateString("6"));
         cmOStringStream ostr;
         cmOStringStream ostr;
-        if ( mit->first != "MacOS" )
+        if (cmtarget.IsFrameworkOnApple())
           {
           {
+          // dstPath in frameworks is relative to Versions/<version>
+          ostr << mit->first;
+          }
+        else if ( mit->first != "MacOS" )
+          {
+          // dstPath in bundles is relative to Contents/MacOS
           ostr << "../" << mit->first.c_str();
           ostr << "../" << mit->first.c_str();
           }
           }
         copyFilesBuildPhase->AddAttribute("dstPath",
         copyFilesBuildPhase->AddAttribute("dstPath",
@@ -1357,11 +1363,6 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
      target.GetType() == cmTarget::EXECUTABLE)
      target.GetType() == cmTarget::EXECUTABLE)
     {
     {
     std::string pndir = target.GetDirectory();
     std::string pndir = target.GetDirectory();
-    if(target.IsFrameworkOnApple())
-      {
-      pndir += "/..";
-      pndir = cmSystemTools::CollapseFullPath(pndir.c_str());
-      }
     buildSettings->AddAttribute("SYMROOT", 
     buildSettings->AddAttribute("SYMROOT", 
                                 this->CreateString(pndir.c_str()));
                                 this->CreateString(pndir.c_str()));
     buildSettings->AddAttribute("EXECUTABLE_PREFIX", 
     buildSettings->AddAttribute("EXECUTABLE_PREFIX", 
@@ -1429,17 +1430,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
       fileType = "wrapper.framework";
       fileType = "wrapper.framework";
       productType = "com.apple.product-type.framework";
       productType = "com.apple.product-type.framework";
 
 
-      const char* version = target.GetProperty("FRAMEWORK_VERSION");
-      if(!version)
-        {
-        version = target.GetProperty("VERSION");
-        }
-      if(!version)
-        {
-        version = "A";
-        }
+      std::string version = target.GetFrameworkVersion();
       buildSettings->AddAttribute("FRAMEWORK_VERSION",
       buildSettings->AddAttribute("FRAMEWORK_VERSION",
-                                  this->CreateString(version));
+                                  this->CreateString(version.c_str()));
       }
       }
     else
     else
       {
       {
@@ -1649,18 +1642,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
   if(target.GetType() == cmTarget::SHARED_LIBRARY)
   if(target.GetType() == cmTarget::SHARED_LIBRARY)
     {
     {
     // Get the install_name directory for the build tree.
     // Get the install_name directory for the build tree.
-    install_name_dir = target.GetInstallNameDirForBuildTree(configName);
-    if(target.GetPropertyAsBool("FRAMEWORK"))
-      {
-      if(install_name_dir.find(".framework") != install_name_dir.npos)
-        {
-        install_name_dir = install_name_dir + "/..";
-        install_name_dir = 
-            cmSystemTools::CollapseFullPath(install_name_dir.c_str());
-        //std::cerr << "new install name " << install_name_dir << "\n";
-        }
-      }
-    
+    install_name_dir = target.GetInstallNameDirForBuildTree(configName, true);
     if(install_name_dir.empty())
     if(install_name_dir.empty())
       {
       {
       // Xcode will not pass the -install_name option at all if INSTALL_PATH
       // Xcode will not pass the -install_name option at all if INSTALL_PATH
@@ -2868,26 +2850,9 @@ cmGlobalXCodeGenerator
     {
     {
     if(config)
     if(config)
       {
       {
-      if(dir.find(".framework") != dir.npos)
-        {
-        // Remove trailing slashes (so that the rfind does not find the one at
-        // the very end...!)
-        //
-        cmSystemTools::ConvertToUnixSlashes(dir);
-        std::string::size_type pos = dir.rfind("/");
-        std::string framework = dir.substr(pos);
-        std::string newDir = dir.substr(0, pos);
-        newDir += "/";
-        newDir += config;
-        dir = newDir;
-        dir += framework;
-        }
-      else
-        {
-        dir += prefix;
-        dir += config;
-        dir += suffix;
-        }
+      dir += prefix;
+      dir += config;
+      dir += suffix;
       }
       }
     }
     }
 }
 }

+ 5 - 4
Source/cmInstallTargetGenerator.cxx

@@ -239,16 +239,17 @@ cmInstallTargetGenerator
 
 
       // Compute the build tree location of the framework directory
       // Compute the build tree location of the framework directory
       std::string from1 = fromDirConfig;
       std::string from1 = fromDirConfig;
-      // Remove trailing slashes... so that from1 ends with ".framework":
-      //
-      cmSystemTools::ConvertToUnixSlashes(from1);
+      from1 += targetName;
+      from1 += ".framework";
       files.push_back(from1);
       files.push_back(from1);
 
 
       type = cmTarget::INSTALL_DIRECTORY;
       type = cmTarget::INSTALL_DIRECTORY;
 
 
       // Need to apply install_name_tool and stripping to binary
       // Need to apply install_name_tool and stripping to binary
       // inside framework.
       // inside framework.
-      toInstallPath += ".framework/";
+      toInstallPath += ".framework/Versions/";
+      toInstallPath += this->Target->GetFrameworkVersion();
+      toInstallPath += "/";
       toInstallPath += this->GetInstallFilename(this->Target, config,
       toInstallPath += this->GetInstallFilename(this->Target, config,
                                                 NameNormal);
                                                 NameNormal);
 
 

+ 1 - 1
Source/cmLocalVisualStudio7Generator.cxx

@@ -668,7 +668,7 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
   if ( this->Version >= 8 )
   if ( this->Version >= 8 )
     {
     {
     // Check the filesystem type where the target will be written.
     // Check the filesystem type where the target will be written.
-    if(cmLVS6G_IsFAT(target.GetDirectory(configName)))
+    if(cmLVS6G_IsFAT(target.GetDirectory(configName).c_str()))
       {
       {
       // Add a flag telling the manifest tool to use a workaround
       // Add a flag telling the manifest tool to use a workaround
       // for FAT32 file systems, which can cause an empty manifest
       // for FAT32 file systems, which can cause an empty manifest

+ 80 - 58
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -39,20 +39,11 @@ cmMakefileLibraryTargetGenerator
 
 
   if(this->Target->IsFrameworkOnApple())
   if(this->Target->IsFrameworkOnApple())
     {
     {
-    if(const char* fversion = this->Target->GetProperty("FRAMEWORK_VERSION"))
-      {
-      this->FrameworkVersion = fversion;
-      }
-    else if(const char* tversion = this->Target->GetProperty("VERSION"))
-      {
-      this->FrameworkVersion = tversion;
-      }
-    else
-      {
-      this->FrameworkVersion = "A";
-      }
+    this->FrameworkVersion = this->Target->GetFrameworkVersion();
     this->MacContentDirectory = this->Target->GetDirectory();
     this->MacContentDirectory = this->Target->GetDirectory();
-    this->MacContentDirectory += "/Versions/";
+    this->MacContentDirectory += "/";
+    this->MacContentDirectory += this->TargetNameOut;
+    this->MacContentDirectory += ".framework/Versions/";
     this->MacContentDirectory += this->FrameworkVersion;
     this->MacContentDirectory += this->FrameworkVersion;
     this->MacContentDirectory += "/";
     this->MacContentDirectory += "/";
     }
     }
@@ -244,50 +235,82 @@ void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
-void cmMakefileLibraryTargetGenerator::CreateFramework(
-  std::string& targetName,
-  std::string& outpath)
+void cmMakefileLibraryTargetGenerator::CreateFramework()
 {
 {
-  std::string symlink;
-  std::string symlink2;
+  // TODO: Use the cmMakefileTargetGenerator::ExtraFiles vector to
+  // drive rules to create these files at build time.
+  std::string oldName;
+  std::string newName;
+
+  // Compute the location of the top-level foo.framework directory.
+  std::string top = this->Target->GetDirectory();
+  top += "/";
+  top += this->TargetNameOut;
+  top += ".framework/";
+
   // Make foo.framework/Versions
   // Make foo.framework/Versions
-  std::string dir = outpath;
-  dir += "Versions";
-  cmSystemTools::MakeDirectory(dir.c_str());
-  std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
-  // cd foo.framework to setup symlinks with relative paths
-  cmSystemTools::ChangeDirectory((outpath+"Versions").c_str());
+  std::string versions = top;
+  versions += "Versions";
+  cmSystemTools::MakeDirectory(versions.c_str());
+
+  // Make foo.framework/Versions/version
+  std::string version = versions;
+  version += "/";
+  version += this->FrameworkVersion;
+  cmSystemTools::MakeDirectory(version.c_str());
+
   // Current -> version
   // Current -> version
-  symlink = this->FrameworkVersion;
-  symlink2 = "Current";
-  cmSystemTools::RemoveFile("Current");
-  cmSystemTools::CreateSymlink(symlink.c_str(), symlink2.c_str());
-  this->Makefile->AddCMakeOutputFile((outpath + "Versions/Current").c_str());
-  // change to top level of framework to create next set of symlinks
-  cmSystemTools::ChangeDirectory(outpath.c_str());
+  oldName = this->FrameworkVersion;
+  newName = versions;
+  newName += "/Current";
+  cmSystemTools::RemoveFile(newName.c_str());
+  cmSystemTools::CreateSymlink(oldName.c_str(), newName.c_str());
+  this->Makefile->AddCMakeOutputFile(newName.c_str());
+
   // foo -> Versions/Current/foo
   // foo -> Versions/Current/foo
-  symlink = "Versions/Current/";
-  symlink += targetName;
-  symlink2 = targetName;
-  cmSystemTools::CreateSymlink(symlink.c_str(), symlink2.c_str());
-  this->Makefile->AddCMakeOutputFile((outpath + targetName).c_str());
-  // Resources -> Versions/Current/Resources 
-  symlink = "Versions/Current/Resources";
-  symlink2 = "Resources";
-  cmSystemTools::CreateSymlink(symlink.c_str(), symlink2.c_str());
-  this->Makefile->AddCMakeOutputFile((outpath + "Resources").c_str());
+  oldName = "Versions/Current/";
+  oldName += this->TargetNameOut;
+  newName = top;
+  newName += this->TargetNameOut;
+  cmSystemTools::RemoveFile(newName.c_str());
+  cmSystemTools::CreateSymlink(oldName.c_str(), newName.c_str());
+  this->Makefile->AddCMakeOutputFile(newName.c_str());
+
+  // Resources -> Versions/Current/Resources
+  if(this->MacContentFolders.find("Resources") !=
+     this->MacContentFolders.end())
+    {
+    oldName = "Versions/Current/Resources";
+    newName = top;
+    newName += "Resources";
+    cmSystemTools::RemoveFile(newName.c_str());
+    cmSystemTools::CreateSymlink(oldName.c_str(), newName.c_str());
+    this->Makefile->AddCMakeOutputFile(newName.c_str());
+    }
+
   // Headers -> Versions/Current/Headers
   // Headers -> Versions/Current/Headers
-  symlink = "Versions/Current/Headers";
-  symlink2 = "Headers";
-  cmSystemTools::CreateSymlink(symlink.c_str(), symlink2.c_str());
-  this->Makefile->AddCMakeOutputFile((outpath + "Headers").c_str());
+  if(this->MacContentFolders.find("Headers") !=
+     this->MacContentFolders.end())
+    {
+    oldName = "Versions/Current/Headers";
+    newName = top;
+    newName += "Headers";
+    cmSystemTools::RemoveFile(newName.c_str());
+    cmSystemTools::CreateSymlink(oldName.c_str(), newName.c_str());
+    this->Makefile->AddCMakeOutputFile(newName.c_str());
+    }
+
   // PrivateHeaders -> Versions/Current/PrivateHeaders
   // PrivateHeaders -> Versions/Current/PrivateHeaders
-  symlink = "Versions/Current/PrivateHeaders";
-  symlink2 = "PrivateHeaders";
-  cmSystemTools::CreateSymlink(symlink.c_str(), symlink2.c_str());
-  this->Makefile->AddCMakeOutputFile((outpath + "PrivateHeaders").c_str());
-  // go back to where we were
-  cmSystemTools::ChangeDirectory(cwd.c_str());
+  if(this->MacContentFolders.find("PrivateHeaders") !=
+     this->MacContentFolders.end())
+    {
+    oldName = "Versions/Current/PrivateHeaders";
+    newName = top;
+    newName += "PrivateHeaders";
+    cmSystemTools::RemoveFile(newName.c_str());
+    cmSystemTools::CreateSymlink(oldName.c_str(), newName.c_str());
+    this->Makefile->AddCMakeOutputFile(newName.c_str());
+    }
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -354,7 +377,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
   // Construct the full path version of the names.
   // Construct the full path version of the names.
   std::string outpath;
   std::string outpath;
   std::string outpathImp;
   std::string outpathImp;
-  if(relink)
+  if(this->Target->IsFrameworkOnApple())
+    {
+    outpath = this->MacContentDirectory;
+    this->CreateFramework();
+    }
+  else if(relink)
     {
     {
     outpath = this->Makefile->GetStartOutputDirectory();
     outpath = this->Makefile->GetStartOutputDirectory();
     outpath += cmake::GetCMakeFilesDirectory();
     outpath += cmake::GetCMakeFilesDirectory();
@@ -379,12 +407,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
       }
       }
     }
     }
 
 
-  // If we're creating a framework, place the output into a framework directory
-  if(this->Target->IsFrameworkOnApple())
-    {
-    this->CreateFramework(targetName, outpath);
-    }
-
   std::string targetFullPath = outpath + targetName;
   std::string targetFullPath = outpath + targetName;
   std::string targetFullPathPDB = outpath + targetNamePDB;
   std::string targetFullPathPDB = outpath + targetNamePDB;
   std::string targetFullPathSO = outpath + targetNameSO;
   std::string targetFullPathSO = outpath + targetNameSO;

+ 1 - 2
Source/cmMakefileLibraryTargetGenerator.h

@@ -37,8 +37,7 @@ protected:
                          bool relink);
                          bool relink);
   // MacOSX Framework support methods
   // MacOSX Framework support methods
   void WriteFrameworkRules(bool relink);
   void WriteFrameworkRules(bool relink);
-  void CreateFramework(std::string& targetName,
-                       std::string& outpath);
+  void CreateFramework();
 
 
   // Store the computd framework version for OS X Frameworks.
   // Store the computd framework version for OS X Frameworks.
   std::string FrameworkVersion;
   std::string FrameworkVersion;

+ 5 - 5
Source/cmMakefileTargetGenerator.cxx

@@ -334,6 +334,9 @@ void cmMakefileTargetGenerator::WriteMacOSXContentRules(cmSourceFile& source,
   macdir += pkgloc;
   macdir += pkgloc;
   cmSystemTools::MakeDirectory(macdir.c_str());
   cmSystemTools::MakeDirectory(macdir.c_str());
 
 
+  // Record use of this content location.
+  this->MacContentFolders.insert(pkgloc);
+
   // Get the input file location.
   // Get the input file location.
   std::string input = source.GetFullPath();
   std::string input = source.GetFullPath();
 
 
@@ -1456,11 +1459,8 @@ void cmMakefileTargetGenerator
       if(cmTarget* tgt =
       if(cmTarget* tgt =
          this->GlobalGenerator->FindTarget(0, lib->first.c_str()))
          this->GlobalGenerator->FindTarget(0, lib->first.c_str()))
         {
         {
-        if(const char* location =
-           tgt->GetLocation(this->LocalGenerator->ConfigurationName.c_str()))
-          {
-          depends.push_back(location);
-          }
+        const char* config = this->LocalGenerator->ConfigurationName.c_str();
+        depends.push_back(tgt->GetFullPath(config, false));
         }
         }
       // depend on full path libs as well
       // depend on full path libs as well
       else if(cmSystemTools::FileIsFullPath(lib->first.c_str()))
       else if(cmSystemTools::FileIsFullPath(lib->first.c_str()))

+ 1 - 0
Source/cmMakefileTargetGenerator.h

@@ -204,6 +204,7 @@ protected:
 
 
   // Mac OS X content info.
   // Mac OS X content info.
   std::string MacContentDirectory;
   std::string MacContentDirectory;
+  std::set<cmStdString> MacContentFolders;
 
 
   // Target-wide Fortran module output directory.
   // Target-wide Fortran module output directory.
   bool FortranModuleDirectoryComputed;
   bool FortranModuleDirectoryComputed;

+ 174 - 138
Source/cmTarget.cxx

@@ -1626,42 +1626,30 @@ void cmTarget::MarkAsImported()
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
-const char* cmTarget::GetDirectory(const char* config, bool implib)
+std::string cmTarget::GetDirectory(const char* config, bool implib)
 {
 {
   if (this->IsImported())
   if (this->IsImported())
     {
     {
-    return this->ImportedGetDirectory(config, implib);
-    }
-  else
-    {
-    return this->NormalGetDirectory(config, implib);
-    }
-}
-
-//----------------------------------------------------------------------------
-const char* cmTarget::ImportedGetDirectory(const char* config, bool implib)
-{
-  this->Directory =
-    cmSystemTools::GetFilenamePath(
+    // Return the directory from which the target is imported.
+    return
+      cmSystemTools::GetFilenamePath(
       this->ImportedGetFullPath(config, implib));
       this->ImportedGetFullPath(config, implib));
-  return this->Directory.c_str();
-}
-
-//----------------------------------------------------------------------------
-const char* cmTarget::NormalGetDirectory(const char* config, bool implib)
-{
-  if(config && *config)
-    {
-    // Do not create the directory when config is given:
-    this->Directory = this->GetOutputDir(implib);
-    // Add the configuration's subdirectory.
-    this->Makefile->GetLocalGenerator()->GetGlobalGenerator()->
-      AppendDirectoryForConfig("/", config, "", this->Directory);
-    return this->Directory.c_str();
     }
     }
   else
   else
     {
     {
-    return this->GetOutputDir(implib);
+    // Return the directory in which the target will be built.
+    if(config && *config)
+      {
+      // Add the configuration's subdirectory.
+      std::string dir = this->GetOutputDir(implib);
+      this->Makefile->GetLocalGenerator()->GetGlobalGenerator()->
+        AppendDirectoryForConfig("/", config, "", dir);
+      return dir;
+      }
+    else
+      {
+      return this->GetOutputDir(implib);
+      }
     }
     }
 }
 }
 
 
@@ -1688,22 +1676,31 @@ const char* cmTarget::ImportedGetLocation(const char* config)
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 const char* cmTarget::NormalGetLocation(const char* config)
 const char* cmTarget::NormalGetLocation(const char* config)
 {
 {
-  this->Location = this->GetDirectory(config);
+  // Handle the configuration-specific case first.
+  if(config)
+    {
+    this->Location = this->GetFullPath(config, false);
+    return this->Location.c_str();
+    }
+
+  // Now handle the deprecated build-time configuration location.
+  this->Location = this->GetDirectory();
   if(!this->Location.empty())
   if(!this->Location.empty())
     {
     {
     this->Location += "/";
     this->Location += "/";
     }
     }
-  if(!config)
+  const char* cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR");
+  if(cfgid && strcmp(cfgid, ".") != 0)
     {
     {
-    // No specific configuration was given so it will not appear on
-    // the result of GetDirectory.  Add a name here to be replaced at
-    // build time.
-    const char* cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR");
-    if(cfgid && strcmp(cfgid, ".") != 0)
-      {
-      this->Location += cfgid;
-      this->Location += "/";
-      }
+    this->Location += cfgid;
+    this->Location += "/";
+    }
+   if(this->IsFrameworkOnApple())
+    {
+    this->Location += this->GetFullName(config, false);
+    this->Location += ".framework/Versions/";
+    this->Location += this->GetFrameworkVersion();
+    this->Location += "/";
     }
     }
   this->Location += this->GetFullName(config, false);
   this->Location += this->GetFullName(config, false);
   return this->Location.c_str();
   return this->Location.c_str();
@@ -2203,6 +2200,14 @@ std::string cmTarget::NormalGetFullPath(const char* config, bool implib,
   std::string fpath = this->GetDirectory(config, implib);
   std::string fpath = this->GetDirectory(config, implib);
   fpath += "/";
   fpath += "/";
 
 
+  if(this->IsFrameworkOnApple())
+    {
+    fpath += this->GetFullName(config, false);
+    fpath += ".framework/Versions/";
+    fpath += this->GetFrameworkVersion();
+    fpath += "/";
+    }
+
   // Add the full name of the target.
   // Add the full name of the target.
   if(implib)
   if(implib)
     {
     {
@@ -2474,7 +2479,8 @@ void cmTarget::GetLibraryNamesInternal(std::string& name,
   const char* version = this->GetProperty("VERSION");
   const char* version = this->GetProperty("VERSION");
   const char* soversion = this->GetProperty("SOVERSION");
   const char* soversion = this->GetProperty("SOVERSION");
   if((type != cmTarget::SHARED_LIBRARY && type != cmTarget::MODULE_LIBRARY) ||
   if((type != cmTarget::SHARED_LIBRARY && type != cmTarget::MODULE_LIBRARY) ||
-     !this->Makefile->GetDefinition(sonameFlag.c_str()))
+     !this->Makefile->GetDefinition(sonameFlag.c_str()) ||
+     this->IsFrameworkOnApple())
     {
     {
     // Versioning is supported only for shared libraries and modules,
     // Versioning is supported only for shared libraries and modules,
     // and then only when the platform supports an soname flag.
     // and then only when the platform supports an soname flag.
@@ -2801,13 +2807,14 @@ bool cmTarget::NeedRelinkBeforeInstall()
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
-std::string cmTarget::GetInstallNameDirForBuildTree(const char* config)
+std::string cmTarget::GetInstallNameDirForBuildTree(const char* config,
+                                                    bool for_xcode)
 {
 {
   // If building directly for installation then the build tree install_name
   // If building directly for installation then the build tree install_name
   // is the same as the install tree.
   // is the same as the install tree.
   if(this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"))
   if(this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"))
     {
     {
-    return GetInstallNameDirForInstallTree(config);
+    return GetInstallNameDirForInstallTree(config, for_xcode);
     }
     }
 
 
   // Use the build tree directory for the target.
   // Use the build tree directory for the target.
@@ -2817,6 +2824,13 @@ std::string cmTarget::GetInstallNameDirForBuildTree(const char* config)
     {
     {
     std::string dir = this->GetDirectory(config);
     std::string dir = this->GetDirectory(config);
     dir += "/";
     dir += "/";
+    if(this->IsFrameworkOnApple() && !for_xcode)
+      {
+      dir += this->GetFullName(config, false);
+      dir += ".framework/Versions/";
+      dir += this->GetFrameworkVersion();
+      dir += "/";
+      }
     return dir;
     return dir;
     }
     }
   else
   else
@@ -2826,7 +2840,8 @@ std::string cmTarget::GetInstallNameDirForBuildTree(const char* config)
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
-std::string cmTarget::GetInstallNameDirForInstallTree(const char*)
+std::string cmTarget::GetInstallNameDirForInstallTree(const char* config,
+                                                      bool for_xcode)
 {
 {
   // Lookup the target property.
   // Lookup the target property.
   const char* install_name_dir = this->GetProperty("INSTALL_NAME_DIR");
   const char* install_name_dir = this->GetProperty("INSTALL_NAME_DIR");
@@ -2836,6 +2851,13 @@ std::string cmTarget::GetInstallNameDirForInstallTree(const char*)
     {
     {
     std::string dir = install_name_dir;
     std::string dir = install_name_dir;
     dir += "/";
     dir += "/";
+    if(this->IsFrameworkOnApple() && !for_xcode)
+      {
+      dir += this->GetFullName(config, false);
+      dir += ".framework/Versions/";
+      dir += this->GetFrameworkVersion();
+      dir += "/";
+      }
     return dir;
     return dir;
     }
     }
   else
   else
@@ -2845,7 +2867,7 @@ std::string cmTarget::GetInstallNameDirForInstallTree(const char*)
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
-const char* cmTarget::GetOutputDir(bool implib)
+std::string cmTarget::GetOutputDir(bool implib)
 {
 {
   // The implib option is only allowed for shared libraries, module
   // The implib option is only allowed for shared libraries, module
   // libraries, and executables.
   // libraries, and executables.
@@ -2879,62 +2901,36 @@ const char* cmTarget::GetOutputDir(bool implib)
                    msg.c_str());
                    msg.c_str());
     }
     }
 
 
+  return this->ComputeBaseOutputDir(implib);
+}
+
+//----------------------------------------------------------------------------
+std::string const& cmTarget::ComputeBaseOutputDir(bool implib)
+{
   // Select whether we are constructing the directory for the main
   // Select whether we are constructing the directory for the main
   // target or the import library.
   // target or the import library.
-  std::string& out = implib? this->OutputDirImplib : this->OutputDir;
+  std::string& out = implib? this->BaseOutputDirImplib : this->BaseOutputDir;
 
 
-  if(out.empty())
+  // Return immediately if the directory has already been computed.
+  if(!out.empty())
+    {
+    return out;
+    }
+
+  // Look for a target property defining the target output directory
+  // based on the target type.
+  const char* propertyName = 0;
+  switch(this->GetType())
     {
     {
-    // Look for a target property defining the target output directory
-    // based on the target type.
-    const char* propertyName = 0;
-    switch(this->GetType())
+    case cmTarget::SHARED_LIBRARY:
       {
       {
-      case cmTarget::SHARED_LIBRARY:
-        {
-        // For non-DLL platforms shared libraries are treated as
-        // library targets.  For DLL platforms the DLL part of a
-        // shared library is treated as a runtime target and the
-        // corresponding import library is treated as an archive
-        // target.
-        if(this->DLLPlatform)
-          {
-          if(implib)
-            {
-            propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
-            }
-          else
-            {
-            propertyName = "RUNTIME_OUTPUT_DIRECTORY";
-            }
-          }
-        else
-          {
-          propertyName = "LIBRARY_OUTPUT_DIRECTORY";
-          }
-        } break;
-      case cmTarget::STATIC_LIBRARY:
-        {
-        // Static libraries are always treated as archive targets.
-        propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
-        } break;
-      case cmTarget::MODULE_LIBRARY:
-        {
-        // Module libraries are always treated as library targets.
-        // Module import libraries are treated as archive targets.
-        if(implib)
-          {
-          propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
-          }
-        else
-          {
-          propertyName = "LIBRARY_OUTPUT_DIRECTORY";
-          }
-        } break;
-      case cmTarget::EXECUTABLE:
+      // For non-DLL platforms shared libraries are treated as
+      // library targets.  For DLL platforms the DLL part of a
+      // shared library is treated as a runtime target and the
+      // corresponding import library is treated as an archive
+      // target.
+      if(this->DLLPlatform)
         {
         {
-        // Executables are always treated as runtime targets.
-        // Executable import libraries are treated as archive targets.
         if(implib)
         if(implib)
           {
           {
           propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
           propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
@@ -2943,53 +2939,93 @@ const char* cmTarget::GetOutputDir(bool implib)
           {
           {
           propertyName = "RUNTIME_OUTPUT_DIRECTORY";
           propertyName = "RUNTIME_OUTPUT_DIRECTORY";
           }
           }
-        } break;
-      default: break;
-      }
-
-    // Select an output directory.
-    if(const char* outdir = this->GetProperty(propertyName))
-      {
-      // Use the user-specified output directory.
-      out = outdir;
-      }
-    else if(this->GetType() == cmTarget::EXECUTABLE)
-      {
-      // Lookup the output path for executables.
-      out = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");
-      }
-    else if(this->GetType() == cmTarget::STATIC_LIBRARY ||
-            this->GetType() == cmTarget::SHARED_LIBRARY ||
-            this->GetType() == cmTarget::MODULE_LIBRARY)
+        }
+      else
+        {
+        propertyName = "LIBRARY_OUTPUT_DIRECTORY";
+        }
+      } break;
+    case cmTarget::STATIC_LIBRARY:
       {
       {
-      // Lookup the output path for libraries.
-      out = this->Makefile->GetSafeDefinition("LIBRARY_OUTPUT_PATH");
-      }
-    if(out.empty())
+      // Static libraries are always treated as archive targets.
+      propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
+      } break;
+    case cmTarget::MODULE_LIBRARY:
       {
       {
-      // Default to the current output directory.
-      out = ".";
-      }
-    // Convert the output path to a full path in case it is
-    // specified as a relative path.  Treat a relative path as
-    // relative to the current output directory for this makefile.
-    out =
-      cmSystemTools::CollapseFullPath
-      (out.c_str(), this->Makefile->GetStartOutputDirectory());
-
-    // TODO: Make AppBundle and Framework directory computation in
-    // target consistent.  Why do we add the .framework part here for
-    // frameworks but not the .app part for bundles?  We should
-    // probably not add it for either.
-    if(this->IsFrameworkOnApple())
+      // Module libraries are always treated as library targets.
+      // Module import libraries are treated as archive targets.
+      if(implib)
+        {
+        propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
+        }
+      else
+        {
+        propertyName = "LIBRARY_OUTPUT_DIRECTORY";
+        }
+      } break;
+    case cmTarget::EXECUTABLE:
       {
       {
-      out += "/";
-      out += this->GetFullName(0, implib);
-      out += ".framework";
-      }
-  }
+      // Executables are always treated as runtime targets.
+      // Executable import libraries are treated as archive targets.
+      if(implib)
+        {
+        propertyName = "ARCHIVE_OUTPUT_DIRECTORY";
+        }
+      else
+        {
+        propertyName = "RUNTIME_OUTPUT_DIRECTORY";
+        }
+      } break;
+    default: break;
+    }
 
 
-  return out.c_str();
+  // Select an output directory.
+  if(const char* outdir = this->GetProperty(propertyName))
+    {
+    // Use the user-specified output directory.
+    out = outdir;
+    }
+  else if(this->GetType() == cmTarget::EXECUTABLE)
+    {
+    // Lookup the output path for executables.
+    out = this->Makefile->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");
+    }
+  else if(this->GetType() == cmTarget::STATIC_LIBRARY ||
+          this->GetType() == cmTarget::SHARED_LIBRARY ||
+          this->GetType() == cmTarget::MODULE_LIBRARY)
+    {
+    // Lookup the output path for libraries.
+    out = this->Makefile->GetSafeDefinition("LIBRARY_OUTPUT_PATH");
+    }
+  if(out.empty())
+    {
+    // Default to the current output directory.
+    out = ".";
+    }
+
+  // Convert the output path to a full path in case it is
+  // specified as a relative path.  Treat a relative path as
+  // relative to the current output directory for this makefile.
+  out = (cmSystemTools::CollapseFullPath
+         (out.c_str(), this->Makefile->GetStartOutputDirectory()));
+  return out;
+}
+
+//----------------------------------------------------------------------------
+std::string cmTarget::GetFrameworkVersion()
+{
+  if(const char* fversion = this->GetProperty("FRAMEWORK_VERSION"))
+    {
+    return fversion;
+    }
+  else if(const char* tversion = this->GetProperty("VERSION"))
+    {
+    return tversion;
+    }
+  else
+    {
+    return "A";
+    }
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------

+ 13 - 10
Source/cmTarget.h

@@ -253,7 +253,7 @@ public:
       configuration name is given then the generator will add its
       configuration name is given then the generator will add its
       subdirectory for that configuration.  Otherwise just the canonical
       subdirectory for that configuration.  Otherwise just the canonical
       output directory is given.  */
       output directory is given.  */
-  const char* GetDirectory(const char* config = 0, bool implib = false);
+  std::string GetDirectory(const char* config = 0, bool implib = false);
 
 
   /** Get the location of the target in the build tree for the given
   /** Get the location of the target in the build tree for the given
       configuration.  This location is suitable for use as the LOCATION
       configuration.  This location is suitable for use as the LOCATION
@@ -348,8 +348,10 @@ public:
   /** Return true if builtin chrpath will work for this target */
   /** Return true if builtin chrpath will work for this target */
   bool IsChrpathUsed();
   bool IsChrpathUsed();
 
 
-  std::string GetInstallNameDirForBuildTree(const char* config);
-  std::string GetInstallNameDirForInstallTree(const char* config);
+  std::string GetInstallNameDirForBuildTree(const char* config,
+                                            bool for_xcode = false);
+  std::string GetInstallNameDirForInstallTree(const char* config,
+                                              bool for_xcode = false);
 
 
   cmComputeLinkInformation* GetLinkInformation(const char* config);
   cmComputeLinkInformation* GetLinkInformation(const char* config);
 
 
@@ -384,6 +386,10 @@ public:
   /** Return whether this target is an executable Bundle on Apple.  */
   /** Return whether this target is an executable Bundle on Apple.  */
   bool IsAppBundleOnApple();
   bool IsAppBundleOnApple();
 
 
+  /** Return the framework version string.  Undefined if
+      IsFrameworkOnApple returns false.  */
+  std::string GetFrameworkVersion();
+
   /** Get a backtrace from the creation of the target.  */
   /** Get a backtrace from the creation of the target.  */
   cmListFileBacktrace const& GetBacktrace() const;
   cmListFileBacktrace const& GetBacktrace() const;
 
 
@@ -464,16 +470,14 @@ private:
   void SetPropertyDefault(const char* property, const char* default_value);
   void SetPropertyDefault(const char* property, const char* default_value);
 
 
   // Get the full path to the target output directory.
   // Get the full path to the target output directory.
-  const char* GetOutputDir(bool implib);
+  std::string GetOutputDir(bool implib);
+  std::string const& cmTarget::ComputeBaseOutputDir(bool implib);
 
 
   const char* ImportedGetLocation(const char* config);
   const char* ImportedGetLocation(const char* config);
   const char* NormalGetLocation(const char* config);
   const char* NormalGetLocation(const char* config);
 
 
   std::string GetFullNameImported(const char* config, bool implib);
   std::string GetFullNameImported(const char* config, bool implib);
 
 
-  const char* ImportedGetDirectory(const char* config, bool implib);
-  const char* NormalGetDirectory(const char* config, bool implib);
-
   std::string ImportedGetFullPath(const char* config, bool implib);
   std::string ImportedGetFullPath(const char* config, bool implib);
   std::string NormalGetFullPath(const char* config, bool implib,
   std::string NormalGetFullPath(const char* config, bool implib,
                                 bool realname);
                                 bool realname);
@@ -500,9 +504,8 @@ private:
   bool HaveInstallRule;
   bool HaveInstallRule;
   std::string InstallPath;
   std::string InstallPath;
   std::string RuntimeInstallPath;
   std::string RuntimeInstallPath;
-  std::string OutputDir;
-  std::string OutputDirImplib;
-  std::string Directory;
+  std::string BaseOutputDir;
+  std::string BaseOutputDirImplib;
   std::string Location;
   std::string Location;
   std::string ExportMacro;
   std::string ExportMacro;
   std::set<cmStdString> Utilities;
   std::set<cmStdString> Utilities;

+ 5 - 1
Tests/Framework/CMakeLists.txt

@@ -10,8 +10,11 @@ add_library(foo SHARED
   fooNeither.h
   fooNeither.h
   fooBoth.h
   fooBoth.h
   test.lua
   test.lua
+  fooDeepPublic.h
 )
 )
-
+set_property(SOURCE fooDeepPublic.h
+  PROPERTY MACOSX_PACKAGE_LOCATION Headers/Deep
+  )
 set(foo_ver ver4)
 set(foo_ver ver4)
 
 
 set_target_properties(foo PROPERTIES
 set_target_properties(foo PROPERTIES
@@ -20,6 +23,7 @@ set_target_properties(foo PROPERTIES
   PRIVATE_HEADER "fooPrivate.h;fooBoth.h"
   PRIVATE_HEADER "fooPrivate.h;fooBoth.h"
   PUBLIC_HEADER "foo.h;foo2.h;fooPublic.h;fooBoth.h"
   PUBLIC_HEADER "foo.h;foo2.h;fooPublic.h;fooBoth.h"
   RESOURCE "test.lua"
   RESOURCE "test.lua"
+  INSTALL_NAME_DIR "@executable_path/../../../Library/Frameworks"
 )
 )
 # fooBoth.h is listed as both public and private... (private wins...)
 # fooBoth.h is listed as both public and private... (private wins...)
 # fooNeither.h is listed as neither public nor private...
 # fooNeither.h is listed as neither public nor private...

+ 1 - 0
Tests/Framework/fooDeepPublic.h

@@ -0,0 +1 @@
+fooDeepPublic