Selaa lähdekoodia

ENH: initial support for creation of frameworks on Mac

Bill Hoffman 18 vuotta sitten
vanhempi
sitoutus
9323a27989

+ 28 - 0
Modules/MacOSXFrameworkInfo.plist.in

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${MACOSX_FRAMEWORK_NAME}</string>
+	<key>CFBundleGetInfoString</key>
+	<string>${MACOSX_FRAMEWORK_INFO_STRING}</string>
+	<key>CFBundleIdentifier</key>
+	<string>${MACOSX_FRAMEWORK_IDENTIFIER}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>${MACOSX_FRAMEWORK_SHORT_VERSION_STRING}</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>${MACOSX_FRAMEWORK_BUNDLE_VERSION}</string>
+	<key>CSResourcesFileMapped</key>
+	<true/>
+	<key>NSHumanReadableCopyright</key>
+	<string>${MACOSX_FRAMEWORK_COPYRIGHT}</string>
+</dict>
+</plist>

+ 5 - 0
Modules/Platform/Darwin.cmake

@@ -76,6 +76,11 @@ SET(CMAKE_C_CREATE_SHARED_MODULE
 SET(CMAKE_Fortran_CREATE_SHARED_MODULE
       "<CMAKE_Fortran_COMPILER>  <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS> <LINK_FLAGS> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>")
 
+SET(CMAKE_C_CREATE_MACOSX_FRAMEWORK
+      "<CMAKE_C_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <LINK_FLAGS> -o <TARGET> -install_name <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
+SET(CMAKE_CXX_CREATE_MACOSX_FRAMEWORK
+      "<CMAKE_CXX_COMPILER> <LANGUAGE_COMPILE_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <LINK_FLAGS> -o <TARGET> -install_name <TARGET_INSTALLNAME_DIR><TARGET_SONAME> <OBJECTS> <LINK_LIBRARIES>")
+
 
  
 SET(CMAKE_PLATFORM_IMPLICIT_INCLUDE_DIRECTORIES /usr/local/include)

+ 5 - 2
Source/cmLocalGenerator.cxx

@@ -1728,8 +1728,11 @@ void cmLocalGenerator
           // off the per-configuration subdirectory.  The link directory
           // ordering knows how to deal with this.
           linkItem += tgt->GetDirectory(0, implib);
-          linkItem += "/";
-          linkItem += tgt->GetFullName(config, implib);
+          if(!tgt->GetPropertyAsBool("FRAMEWORK"))
+            { 
+            linkItem += "/";
+            linkItem += tgt->GetFullName(config, implib);
+            }
           }
         linkLibraries.push_back(linkItem);
         // For full path, use the true location.

+ 246 - 1
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -111,6 +111,13 @@ void cmMakefileLibraryTargetGenerator::WriteStaticLibraryRules()
 //----------------------------------------------------------------------------
 void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
 {
+#ifdef __APPLE__
+  if(this->Target->GetPropertyAsBool("FRAMEWORK"))
+    {
+    this->WriteFrameworkRules(relink);
+    return;
+    }
+#endif
   const char* linkLanguage =
     this->Target->GetLinkerLanguage(this->GlobalGenerator);
   std::string linkRuleVar = "CMAKE_";
@@ -183,6 +190,238 @@ void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
   this->WriteLibraryRules(linkRuleVar.c_str(), extraFlags.c_str(), relink);
 }
 
+//----------------------------------------------------------------------------
+void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
+{
+  const char* linkLanguage =
+    this->Target->GetLinkerLanguage(this->GlobalGenerator);
+  std::string linkRuleVar = "CMAKE_";
+  if (linkLanguage)
+    {
+    linkRuleVar += linkLanguage;
+    }
+  linkRuleVar += "_CREATE_MACOSX_FRAMEWORK";
+
+  std::string extraFlags;
+  this->LocalGenerator->AppendFlags(extraFlags, 
+                                    this->Target->GetProperty("LINK_FLAGS"));
+  std::string linkFlagsConfig = "LINK_FLAGS_";
+  linkFlagsConfig += 
+    cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName.c_str());
+  this->LocalGenerator->AppendFlags
+    (extraFlags, this->Target->GetProperty(linkFlagsConfig.c_str()));
+  this->LocalGenerator->AddConfigVariableFlags
+    (extraFlags, "CMAKE_MACOSX_FRAMEWORK_LINKER_FLAGS",
+     this->LocalGenerator->ConfigurationName.c_str());
+
+  // TODO: .def files should be supported here also.
+  this->WriteLibraryRules(linkRuleVar.c_str(), extraFlags.c_str(), relink);
+}
+
+
+//----------------------------------------------------------------------------
+void cmMakefileLibraryTargetGenerator::CreateFrameworkLinksAndDirs(
+  std::string& targetName,
+  std::string& outpath,
+  const char* version)
+{
+  std::string symlink;
+  std::string symlink2;
+  // 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());
+  // Current -> version
+  symlink = version;
+  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());
+  // 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());
+  // Libraries -> Versions/Current/Libraries
+  symlink = "Versions/Current/Libraries";
+  symlink2 = "Libraries";
+  cmSystemTools::CreateSymlink(symlink.c_str(), symlink2.c_str());
+  this->Makefile->AddCMakeOutputFile((outpath + "Libraries").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());
+  // go back to where we were
+  cmSystemTools::ChangeDirectory(cwd.c_str());
+}
+
+//----------------------------------------------------------------------------
+void cmMakefileLibraryTargetGenerator::CopyFrameworkPublicHeaders(
+  std::string& targetName,
+  std::string& outpath,
+  const char* /*version*/)
+{
+  std::string fullOutput=  outpath + targetName;
+  const char* headers = this->Target->GetProperty("FRAMEWORK_PUBLIC_HEADERS");
+  if(!headers)
+    {
+    return;
+    }
+  std::vector<std::string> headersVec;
+  cmSystemTools::ExpandListArgument(headers,
+                                    headersVec);
+  cmCustomCommandLines commandLines;
+  std::vector<std::string> depends;
+  for(std::vector<std::string>::iterator i = headersVec.begin();
+      i != headersVec.end(); ++i)
+    {
+    cmCustomCommandLine line;
+    cmSourceFile* sf = this->Makefile->GetOrCreateSource(i->c_str());
+    std::string dest = outpath + "Headers/";
+    dest += sf->GetSourceName();
+    std::string ext = sf->GetSourceExtension();
+    if(ext.size())
+      {
+      dest += ".";
+      dest += sf->GetSourceExtension();
+      }
+    line.push_back("$(CMAKE_COMMAND)");
+    line.push_back("-E");
+    line.push_back("copy_if_different");
+    line.push_back(sf->GetFullPath());
+    depends.push_back(sf->GetFullPath());
+    line.push_back(dest);
+    commandLines.push_back(line);
+    // make sure the target gets rebuilt if any of the headers is removed
+    this->GenerateExtraOutput(dest.c_str(),
+                              fullOutput.c_str());
+    }
+  // add a set of prebuild commands to run on the target
+  this->Makefile->
+    AddCustomCommandToTarget(this->Target->GetName(),
+                             depends,
+                             commandLines,
+                             cmTarget::PRE_BUILD,
+                             "copy files",
+                             this->Makefile->GetCurrentOutputDirectory());
+}
+
+//----------------------------------------------------------------------------
+void cmMakefileLibraryTargetGenerator::CopyFrameworkResources(
+  std::string& targetName,
+  std::string& outpath,
+  const char* /*version*/)
+{
+  std::string fullOutput=  outpath + targetName;
+  const char* resources = this->Target->GetProperty("FRAMEWORK_RESOURCES");
+  if(!resources)
+    {
+    return;
+    }
+  std::vector<std::string> resourcesVec;
+  cmSystemTools::ExpandListArgument(resources,
+                                    resourcesVec);
+  cmCustomCommandLines commandLines;
+  std::vector<std::string> depends;
+  for(std::vector<std::string>::iterator i = resourcesVec.begin();
+      i != resourcesVec.end(); ++i)
+    {
+    cmCustomCommandLine line;
+    cmSourceFile* sf = this->Makefile->GetOrCreateSource(i->c_str());
+    std::string dest = outpath + "Resources/";
+    dest += sf->GetSourceName();
+    std::string ext = sf->GetSourceExtension();
+    if(ext.size())
+      {
+      dest += ".";
+      dest += sf->GetSourceExtension();
+      }
+    line.push_back("$(CMAKE_COMMAND)");
+    line.push_back("-E");
+    line.push_back("copy_if_different");
+    line.push_back(sf->GetFullPath());
+    depends.push_back(sf->GetFullPath());
+    line.push_back(dest);
+    commandLines.push_back(line);
+    // make sure the target gets rebuilt if any of the resources is removed
+    this->GenerateExtraOutput(dest.c_str(),
+                              fullOutput.c_str());
+    }
+  // add a set of prebuild commands to run on the target
+  this->Makefile->
+    AddCustomCommandToTarget(this->Target->GetName(),
+                             depends,
+                             commandLines,
+                             cmTarget::PRE_BUILD,
+                             "copy files",
+                             this->Makefile->GetCurrentOutputDirectory());
+}
+
+//----------------------------------------------------------------------------
+void cmMakefileLibraryTargetGenerator::CreateFramework(
+  std::string& targetName,
+  std::string& outpath)
+{
+  std::string macdir = outpath;
+  const char* version = this->Target->GetProperty("FRAMEWORK_VERSION");
+  if(!version)
+    {
+    version = "A";
+    std::string message = 
+      "Warning: FRAMEWORK_VERSION property not found on ";
+    message += targetName;
+    message += ".  Default to verison A.";
+    cmSystemTools::Message(message.c_str());
+    }
+  // create the symbolic links and directories
+  this->CreateFrameworkLinksAndDirs(targetName,
+                                          outpath,
+                                          version);
+  macdir += "Versions/";
+  macdir += version;
+  macdir += "/";
+  outpath += "Versions/";
+  outpath += version;
+  outpath += "/";
+
+  cmSystemTools::MakeDirectory((macdir + "Libraries").c_str());
+  cmSystemTools::MakeDirectory((macdir + "Headers").c_str());
+  // Configure the Info.plist file.  Note that it needs the executable name
+  // to be set
+  std::string rsrcDir = macdir + "Resources/";
+  cmSystemTools::MakeDirectory(rsrcDir.c_str());
+  this->Makefile->AddDefinition("MACOSX_FRAMEWORK_NAME",
+                                targetName.c_str());
+  std::string f1 = 
+    this->Makefile->GetModulesFile("MacOSXFrameworkInfo.plist.in");
+  if ( f1.size() == 0 )
+    {
+    cmSystemTools::Error(
+      "could not find Mac OSX framework Info.plist template file.");
+    }
+  std::string f2 = rsrcDir + "Info.plist";
+  this->Makefile->ConfigureFile(f1.c_str(), f2.c_str(),
+                                false, false, false);
+  this->CopyFrameworkPublicHeaders(targetName,
+                                   outpath,
+                                   version);
+  this->CopyFrameworkResources(targetName,
+                               outpath,
+                               version);
+}
+
 //----------------------------------------------------------------------------
 void cmMakefileLibraryTargetGenerator::WriteLibraryRules
 (const char* linkRuleVar, const char* extraFlags, bool relink)
@@ -269,6 +508,13 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
       outpathImp += "/";
       }
     }
+#if defined(__APPLE__)
+  // If we're creating a framework, place the output into a framework directory
+  if(this->Target->GetPropertyAsBool("FRAMEWORK"))
+    {
+    this->CreateFramework(targetName, outpath);
+    }
+#endif
   std::string targetFullPath = outpath + targetName;
   std::string targetFullPathPDB = outpath + targetNamePDB;
   std::string targetFullPathSO = outpath + targetNameSO;
@@ -510,7 +756,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
                                   this->Makefile->GetHomeOutputDirectory());
     commands.insert(commands.end(), commands1.begin(), commands1.end());
     }
-
   // Add the post-build rules when building but not when relinking.
   if(!relink)
     {

+ 13 - 0
Source/cmMakefileLibraryTargetGenerator.h

@@ -35,6 +35,19 @@ protected:
   void WriteModuleLibraryRules(bool relink);
   void WriteLibraryRules(const char *linkRule, const char *extraFlags,
                          bool relink);
+  // MacOSX Framework support methods
+  void WriteFrameworkRules(bool relink);
+  void CreateFramework(std::string& targetName,
+                             std::string& outpath);
+  void CreateFrameworkLinksAndDirs(std::string& targetName,
+                                         std::string& outpath,
+                                         const char* version);
+  void CopyFrameworkPublicHeaders(std::string& targetName,
+                         std::string& outpath,
+                         const char* version);
+  void CopyFrameworkResources(std::string& targetName,
+                         std::string& outpath,
+                         const char* version);
 };
 
 #endif

+ 18 - 1
Source/cmTarget.cxx

@@ -1634,6 +1634,14 @@ void cmTarget::GetFullNameInternal(TargetType type,
     {
     targetSuffix = this->Makefile->GetSafeDefinition(suffixVar);
     }
+#if defined(__APPLE__)
+  // frameworks do not have a prefix or a suffix
+  if(this->GetPropertyAsBool("FRAMEWORK"))
+    {
+    targetPrefix = 0;
+    targetSuffix = 0;
+    }
+#endif
 
   // Begin the final name with the prefix.
   outPrefix = targetPrefix?targetPrefix:"";
@@ -2145,7 +2153,6 @@ const char* cmTarget::GetOutputDir(bool implib)
       // 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.
@@ -2153,6 +2160,16 @@ const char* cmTarget::GetOutputDir(bool implib)
       cmSystemTools::CollapseFullPath
       (out.c_str(), this->Makefile->GetStartOutputDirectory());
 
+#if defined(__APPLE__)
+    // frameworks do not have a prefix or a suffix
+    if(this->GetPropertyAsBool("FRAMEWORK"))
+      {
+      out += "/";
+      out += this->GetFullName(0, implib);
+      out += ".framework";
+      }
+#endif
+
     // Make sure the output path exists on disk.
     if(!cmSystemTools::MakeDirectory(out.c_str()))
       {