Commit 67834f2d authored by Brad King's avatar Brad King 💬
Browse files

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
parent 5c3a5daa
......@@ -611,18 +611,8 @@ void cmComputeLinkInformation::AddItem(std::string const& item, cmTarget* tgt)
std::string lib = tgt->GetFullPath(config, implib, true);
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
......@@ -1023,7 +1013,7 @@ void cmComputeLinkInformation::AddTargetItem(std::string const& item,
// For compatibility with CMake 2.4 include the item's directory in
// the linker search path.
if(this->OldLinkDirMode &&
if(this->OldLinkDirMode && !target->IsFrameworkOnApple() &&
this->OldLinkDirMask.find(cmSystemTools::GetFilenamePath(item)) ==
this->OldLinkDirMask.end())
{
......
......@@ -803,8 +803,14 @@ cmGlobalXCodeGenerator::CreateXCodeTargets(cmLocalGenerator* gen,
copyFilesBuildPhase->AddAttribute("dstSubfolderSpec",
this->CreateString("6"));
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();
}
copyFilesBuildPhase->AddAttribute("dstPath",
......@@ -1357,11 +1363,6 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
target.GetType() == cmTarget::EXECUTABLE)
{
std::string pndir = target.GetDirectory();
if(target.IsFrameworkOnApple())
{
pndir += "/..";
pndir = cmSystemTools::CollapseFullPath(pndir.c_str());
}
buildSettings->AddAttribute("SYMROOT",
this->CreateString(pndir.c_str()));
buildSettings->AddAttribute("EXECUTABLE_PREFIX",
......@@ -1429,17 +1430,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
fileType = "wrapper.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",
this->CreateString(version));
this->CreateString(version.c_str()));
}
else
{
......@@ -1649,18 +1642,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target,
if(target.GetType() == cmTarget::SHARED_LIBRARY)
{
// 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())
{
// Xcode will not pass the -install_name option at all if INSTALL_PATH
......@@ -2868,26 +2850,9 @@ cmGlobalXCodeGenerator
{
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;
}
}
}
......
......@@ -239,16 +239,17 @@ cmInstallTargetGenerator
// Compute the build tree location of the framework directory
std::string from1 = fromDirConfig;
// Remove trailing slashes... so that from1 ends with ".framework":
//
cmSystemTools::ConvertToUnixSlashes(from1);
from1 += targetName;
from1 += ".framework";
files.push_back(from1);
type = cmTarget::INSTALL_DIRECTORY;
// Need to apply install_name_tool and stripping to binary
// inside framework.
toInstallPath += ".framework/";
toInstallPath += ".framework/Versions/";
toInstallPath += this->Target->GetFrameworkVersion();
toInstallPath += "/";
toInstallPath += this->GetInstallFilename(this->Target, config,
NameNormal);
......
......@@ -668,7 +668,7 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
if ( this->Version >= 8 )
{
// 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
// for FAT32 file systems, which can cause an empty manifest
......
......@@ -39,20 +39,11 @@ cmMakefileLibraryTargetGenerator
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 += "/Versions/";
this->MacContentDirectory += "/";
this->MacContentDirectory += this->TargetNameOut;
this->MacContentDirectory += ".framework/Versions/";
this->MacContentDirectory += this->FrameworkVersion;
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
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
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
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
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
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.
std::string outpath;
std::string outpathImp;
if(relink)
if(this->Target->IsFrameworkOnApple())
{
outpath = this->MacContentDirectory;
this->CreateFramework();
}
else if(relink)
{
outpath = this->Makefile->GetStartOutputDirectory();
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 targetFullPathPDB = outpath + targetNamePDB;
std::string targetFullPathSO = outpath + targetNameSO;
......
......@@ -37,8 +37,7 @@ protected:
bool relink);
// MacOSX Framework support methods
void WriteFrameworkRules(bool relink);
void CreateFramework(std::string& targetName,
std::string& outpath);
void CreateFramework();
// Store the computd framework version for OS X Frameworks.
std::string FrameworkVersion;
......
......@@ -334,6 +334,9 @@ void cmMakefileTargetGenerator::WriteMacOSXContentRules(cmSourceFile& source,
macdir += pkgloc;
cmSystemTools::MakeDirectory(macdir.c_str());
// Record use of this content location.
this->MacContentFolders.insert(pkgloc);
// Get the input file location.
std::string input = source.GetFullPath();
......@@ -1456,11 +1459,8 @@ void cmMakefileTargetGenerator
if(cmTarget* tgt =
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
else if(cmSystemTools::FileIsFullPath(lib->first.c_str()))
......
......@@ -204,6 +204,7 @@ protected:
// Mac OS X content info.
std::string MacContentDirectory;
std::set<cmStdString> MacContentFolders;
// Target-wide Fortran module output directory.
bool FortranModuleDirectoryComputed;
......
......@@ -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())
{
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));
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
{
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)
{
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())
{
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);
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);
fpath += "/";
if(this->IsFrameworkOnApple())
{
fpath += this->GetFullName(config, false);
fpath += ".framework/Versions/";
fpath += this->GetFrameworkVersion();
fpath += "/";
}
// Add the full name of the target.
if(implib)
{
......@@ -2474,7 +2479,8 @@ void cmTarget::GetLibraryNamesInternal(std::string& name,
const char* version = this->GetProperty("VERSION");
const char* soversion = this->GetProperty("SOVERSION");
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,
// 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
// is the same as the install tree.
if(this->GetPropertyAsBool("BUILD_WITH_INSTALL_RPATH"))
{
return GetInstallNameDirForInstallTree(config);
return GetInstallNameDirForInstallTree(config, for_xcode);
}
// 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);
dir += "/";
if(this->IsFrameworkOnApple() && !for_xcode)
{
dir += this->GetFullName(config, false);
dir += ".framework/Versions/";
dir += this->GetFrameworkVersion();
dir += "/";
}
return dir;
}
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.
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;
dir += "/";
if(this->IsFrameworkOnApple() && !for_xcode)
{
dir += this->GetFullName(config, false);
dir += ".framework/Versions/";
dir += this->GetFrameworkVersion();
dir += "/";
}
return dir;
}
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
// libraries, and executables.
......@@ -2879,62 +2901,36 @@ const char* cmTarget::GetOutputDir(bool implib)
msg.c_str());
}
return this->ComputeBaseOutputDir(implib);
}
//----------------------------------------------------------------------------
std::string const& cmTarget::ComputeBaseOutputDir(bool implib)
{
// Select whether we are constructing the directory for the main
// 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