diff --git a/Modules/MacOSXFrameworkInfo.plist.in b/Modules/MacOSXFrameworkInfo.plist.in new file mode 100644 index 000000000..46287aa10 --- /dev/null +++ b/Modules/MacOSXFrameworkInfo.plist.in @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_FRAMEWORK_NAME} + CFBundleGetInfoString + ${MACOSX_FRAMEWORK_INFO_STRING} + CFBundleIdentifier + ${MACOSX_FRAMEWORK_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleShortVersionString + ${MACOSX_FRAMEWORK_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_FRAMEWORK_BUNDLE_VERSION} + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_FRAMEWORK_COPYRIGHT} + + diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake index 11f76d62c..adff35365 100644 --- a/Modules/Platform/Darwin.cmake +++ b/Modules/Platform/Darwin.cmake @@ -76,6 +76,11 @@ SET(CMAKE_C_CREATE_SHARED_MODULE SET(CMAKE_Fortran_CREATE_SHARED_MODULE " -o ") +SET(CMAKE_C_CREATE_MACOSX_FRAMEWORK + " -o -install_name ") +SET(CMAKE_CXX_CREATE_MACOSX_FRAMEWORK + " -o -install_name ") + SET(CMAKE_PLATFORM_IMPLICIT_INCLUDE_DIRECTORIES /usr/local/include) diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 2200102e0..747987196 100644 --- a/Source/cmLocalGenerator.cxx +++ b/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. diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx index 5325c06a0..4eaefce81 100644 --- a/Source/cmMakefileLibraryTargetGenerator.cxx +++ b/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 headersVec; + cmSystemTools::ExpandListArgument(headers, + headersVec); + cmCustomCommandLines commandLines; + std::vector depends; + for(std::vector::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 resourcesVec; + cmSystemTools::ExpandListArgument(resources, + resourcesVec); + cmCustomCommandLines commandLines; + std::vector depends; + for(std::vector::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) { diff --git a/Source/cmMakefileLibraryTargetGenerator.h b/Source/cmMakefileLibraryTargetGenerator.h index 1194afe80..1be2ebd91 100644 --- a/Source/cmMakefileLibraryTargetGenerator.h +++ b/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 diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 5b1542815..85f3d6e0b 100644 --- a/Source/cmTarget.cxx +++ b/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())) {