diff options
author | Brad King <brad.king@kitware.com> | 2016-06-06 15:19:20 (GMT) |
---|---|---|
committer | CMake Topic Stage <kwrobot@kitware.com> | 2016-06-06 15:19:20 (GMT) |
commit | d546456765072e6d4e6307eb6431de23f7bfe418 (patch) | |
tree | ece9c9f378c5be8caf1409ce6fdedb36df7497c6 /Source/CPack | |
parent | c3b268e40d48e25e380ebf2d38edee9a5b604e0c (diff) | |
parent | 63e5eb5fd37691190e80bed142b7f08c3420f625 (diff) | |
download | CMake-d546456765072e6d4e6307eb6431de23f7bfe418.zip CMake-d546456765072e6d4e6307eb6431de23f7bfe418.tar.gz CMake-d546456765072e6d4e6307eb6431de23f7bfe418.tar.bz2 |
Merge topic 'productbuild'
63e5eb5f Help: Add notes for 'productbuild' topic
2e3c67d1 productbuild: Add new productbuild cpack generator.
50a3d340 PackageMaker: factor out common code for creating pkg files.
Diffstat (limited to 'Source/CPack')
-rw-r--r-- | Source/CPack/cmCPackGeneratorFactory.cxx | 5 | ||||
-rw-r--r-- | Source/CPack/cmCPackPKGGenerator.cxx | 367 | ||||
-rw-r--r-- | Source/CPack/cmCPackPKGGenerator.h | 103 | ||||
-rw-r--r-- | Source/CPack/cmCPackPackageMakerGenerator.cxx | 322 | ||||
-rw-r--r-- | Source/CPack/cmCPackPackageMakerGenerator.h | 68 | ||||
-rw-r--r-- | Source/CPack/cmCPackProductBuildGenerator.cxx | 260 | ||||
-rw-r--r-- | Source/CPack/cmCPackProductBuildGenerator.h | 60 |
7 files changed, 799 insertions, 386 deletions
diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx index b17f52e..cf072bb 100644 --- a/Source/CPack/cmCPackGeneratorFactory.cxx +++ b/Source/CPack/cmCPackGeneratorFactory.cxx @@ -28,6 +28,7 @@ #include "cmCPackDragNDropGenerator.h" #include "cmCPackOSXX11Generator.h" #include "cmCPackPackageMakerGenerator.h" +#include "cmCPackProductBuildGenerator.h" #endif #ifdef __CYGWIN__ @@ -122,6 +123,10 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory() this->RegisterGenerator("OSXX11", "Mac OSX X11 bundle", cmCPackOSXX11Generator::CreateGenerator); } + if (cmCPackProductBuildGenerator::CanGenerate()) { + this->RegisterGenerator("productbuild", "Mac OSX pkg", + cmCPackProductBuildGenerator::CreateGenerator); + } #endif #if !defined(_WIN32) && !defined(__QNXNTO__) && !defined(__BEOS__) && \ !defined(__HAIKU__) diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx new file mode 100644 index 0000000..ff0fa8c --- /dev/null +++ b/Source/CPack/cmCPackPKGGenerator.cxx @@ -0,0 +1,367 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackPKGGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> + +cmCPackPKGGenerator::cmCPackPKGGenerator() +{ + this->componentPackageMethod = ONE_PACKAGE; +} + +cmCPackPKGGenerator::~cmCPackPKGGenerator() +{ +} + +bool cmCPackPKGGenerator::SupportsComponentInstallation() const +{ + return true; +} + +int cmCPackPKGGenerator::InitializeInternal() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "cmCPackPKGGenerator::Initialize()" << std::endl); + + return this->Superclass::InitializeInternal(); +} + + +std::string cmCPackPKGGenerator::GetPackageName( + const cmCPackComponent& component) +{ + if (component.ArchiveFile.empty()) { + std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packagesDir += ".dummy"; + std::ostringstream out; + out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) + << "-" << component.Name << ".pkg"; + return out.str(); + } else { + return component.ArchiveFile + ".pkg"; + } +} + +void cmCPackPKGGenerator::WriteDistributionFile( + const char* metapackageFile) +{ + std::string distributionTemplate = + this->FindTemplate("CPack.distribution.dist.in"); + if (distributionTemplate.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: " + << distributionTemplate << std::endl); + return; + } + + std::string distributionFile = metapackageFile; + distributionFile += "/Contents/distribution.dist"; + + // Create the choice outline, which provides a tree-based view of + // the components in their groups. + std::ostringstream choiceOut; + choiceOut << "<choices-outline>" << std::endl; + + // Emit the outline for the groups + std::map<std::string, cmCPackComponentGroup>::iterator groupIt; + for (groupIt = this->ComponentGroups.begin(); + groupIt != this->ComponentGroups.end(); ++groupIt) { + if (groupIt->second.ParentGroup == 0) { + CreateChoiceOutline(groupIt->second, choiceOut); + } + } + + // Emit the outline for the non-grouped components + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + if (!compIt->second.Group) { + choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>" + << std::endl; + } + } + if (!this->PostFlightComponent.Name.empty()) { + choiceOut << "<line choice=\"" << PostFlightComponent.Name + << "Choice\"></line>" << std::endl; + } + choiceOut << "</choices-outline>" << std::endl; + + // Create the actual choices + for (groupIt = this->ComponentGroups.begin(); + groupIt != this->ComponentGroups.end(); ++groupIt) { + CreateChoice(groupIt->second, choiceOut); + } + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + CreateChoice(compIt->second, choiceOut); + } + + if (!this->PostFlightComponent.Name.empty()) { + CreateChoice(PostFlightComponent, choiceOut); + } + + this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str()); + + // Create the distribution.dist file in the metapackage to turn it + // into a distribution package. + this->ConfigureFile(distributionTemplate.c_str(), distributionFile.c_str()); +} + +void cmCPackPKGGenerator::CreateChoiceOutline( + const cmCPackComponentGroup& group, std::ostringstream& out) +{ + out << "<line choice=\"" << group.Name << "Choice\">" << std::endl; + std::vector<cmCPackComponentGroup*>::const_iterator groupIt; + for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end(); + ++groupIt) { + CreateChoiceOutline(**groupIt, out); + } + + std::vector<cmCPackComponent*>::const_iterator compIt; + for (compIt = group.Components.begin(); compIt != group.Components.end(); + ++compIt) { + out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>" + << std::endl; + } + out << "</line>" << std::endl; +} + +void cmCPackPKGGenerator::CreateChoice( + const cmCPackComponentGroup& group, std::ostringstream& out) +{ + out << "<choice id=\"" << group.Name << "Choice\" " + << "title=\"" << group.DisplayName << "\" " + << "start_selected=\"true\" " + << "start_enabled=\"true\" " + << "start_visible=\"true\" "; + if (!group.Description.empty()) { + out << "description=\"" << EscapeForXML(group.Description) << "\""; + } + out << "></choice>" << std::endl; +} + +void cmCPackPKGGenerator::CreateChoice( + const cmCPackComponent& component, std::ostringstream& out) +{ + std::string packageId = "com."; + packageId += this->GetOption("CPACK_PACKAGE_VENDOR"); + packageId += '.'; + packageId += this->GetOption("CPACK_PACKAGE_NAME"); + packageId += '.'; + packageId += component.Name; + + out << "<choice id=\"" << component.Name << "Choice\" " + << "title=\"" << component.DisplayName << "\" " + << "start_selected=\"" + << (component.IsDisabledByDefault && !component.IsRequired ? "false" + : "true") + << "\" " + << "start_enabled=\"" << (component.IsRequired ? "false" : "true") + << "\" " + << "start_visible=\"" << (component.IsHidden ? "false" : "true") + << "\" "; + if (!component.Description.empty()) { + out << "description=\"" << EscapeForXML(component.Description) << "\" "; + } + if (!component.Dependencies.empty() || + !component.ReverseDependencies.empty()) { + // The "selected" expression is evaluated each time any choice is + // selected, for all choices *except* the one that the user + // selected. A component is marked selected if it has been + // selected (my.choice.selected in Javascript) and all of the + // components it depends on have been selected (transitively) or + // if any of the components that depend on it have been selected + // (transitively). Assume that we have components A, B, C, D, and + // E, where each component depends on the previous component (B + // depends on A, C depends on B, D depends on C, and E depends on + // D). The expression we build for the component C will be + // my.choice.selected && B && A || D || E + // This way, selecting C will automatically select everything it depends + // on (B and A), while selecting something that depends on C--either D + // or E--will automatically cause C to get selected. + out << "selected=\"my.choice.selected"; + std::set<const cmCPackComponent*> visited; + AddDependencyAttributes(component, visited, out); + visited.clear(); + AddReverseDependencyAttributes(component, visited, out); + out << "\""; + } + out << ">" << std::endl; + out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl; + out << "</choice>" << std::endl; + + // Create a description of the package associated with this + // component. + std::string relativePackageLocation = "Contents/Packages/"; + relativePackageLocation += this->GetPackageName(component); + + // Determine the installed size of the package. + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + dirName += component.Name; + dirName += this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); + unsigned long installedSize = + component.GetInstalledSizeInKbytes(dirName.c_str()); + + out << "<pkg-ref id=\"" << packageId << "\" " + << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" " + << "installKBytes=\"" << installedSize << "\" " + << ">"; + if (component.IsDownloaded) { + out << this->GetOption("CPACK_DOWNLOAD_SITE") + << this->GetPackageName(component); + } else { + out << "file:./" << relativePackageLocation; + } + out << "</pkg-ref>" << std::endl; +} + +void cmCPackPKGGenerator::AddDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& out) +{ + if (visited.find(&component) != visited.end()) { + return; + } + visited.insert(&component); + + std::vector<cmCPackComponent*>::const_iterator dependIt; + for (dependIt = component.Dependencies.begin(); + dependIt != component.Dependencies.end(); ++dependIt) { + out << " && choices['" << (*dependIt)->Name << "Choice'].selected"; + AddDependencyAttributes(**dependIt, visited, out); + } +} + +void cmCPackPKGGenerator::AddReverseDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& out) +{ + if (visited.find(&component) != visited.end()) { + return; + } + visited.insert(&component); + + std::vector<cmCPackComponent*>::const_iterator dependIt; + for (dependIt = component.ReverseDependencies.begin(); + dependIt != component.ReverseDependencies.end(); ++dependIt) { + out << " || choices['" << (*dependIt)->Name << "Choice'].selected"; + AddReverseDependencyAttributes(**dependIt, visited, out); + } +} + +std::string cmCPackPKGGenerator::EscapeForXML(std::string str) +{ + cmSystemTools::ReplaceString(str, "&", "&"); + cmSystemTools::ReplaceString(str, "<", "<"); + cmSystemTools::ReplaceString(str, ">", ">"); + cmSystemTools::ReplaceString(str, "\"", """); + return str; +} + +bool cmCPackPKGGenerator::CopyCreateResourceFile( + const std::string& name, const std::string& dirName) +{ + std::string uname = cmSystemTools::UpperCase(name); + std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname; + const char* inFileName = this->GetOption(cpackVar.c_str()); + if (!inFileName) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " + << cpackVar.c_str() + << " not specified. It should point to " + << (!name.empty() ? name : "<empty>") << ".rtf, " << name + << ".html, or " << name << ".txt file" << std::endl); + return false; + } + if (!cmSystemTools::FileExists(inFileName)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find " + << (!name.empty() ? name : "<empty>") + << " resource file: " << inFileName << std::endl); + return false; + } + std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName); + if (ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt") { + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Bad file extension specified: " + << ext + << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." + << std::endl); + return false; + } + + std::string destFileName = dirName; + destFileName += '/'; + destFileName += name + ext; + + // Set this so that distribution.dist gets the right name (without + // the path). + this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(), + (name + ext).c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << (inFileName ? inFileName : "(NULL)") << " to " + << destFileName.c_str() << std::endl); + this->ConfigureFile(inFileName, destFileName.c_str()); + return true; +} + +bool cmCPackPKGGenerator::CopyResourcePlistFile( + const std::string& name, const char* outName) +{ + if (!outName) { + outName = name.c_str(); + } + + std::string inFName = "CPack."; + inFName += name; + inFName += ".in"; + std::string inFileName = this->FindTemplate(inFName.c_str()); + if (inFileName.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find input file: " << inFName << std::endl); + return false; + } + + std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + destFileName += "/"; + destFileName += outName; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Configure file: " << inFileName.c_str() << " to " + << destFileName.c_str() << std::endl); + this->ConfigureFile(inFileName.c_str(), destFileName.c_str()); + return true; +} + +int cmCPackPKGGenerator::CopyInstallScript(const std::string& resdir, + const std::string& script, + const std::string& name) +{ + std::string dst = resdir; + dst += "/"; + dst += name; + cmSystemTools::CopyFileAlways(script.c_str(), dst.c_str()); + cmSystemTools::SetPermissions(dst.c_str(), 0777); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "copy script : " + << script << "\ninto " << dst.c_str() << std::endl); + + return 1; +} diff --git a/Source/CPack/cmCPackPKGGenerator.h b/Source/CPack/cmCPackPKGGenerator.h new file mode 100644 index 0000000..3011f78 --- /dev/null +++ b/Source/CPack/cmCPackPKGGenerator.h @@ -0,0 +1,103 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackPKGGenerator_h +#define cmCPackPKGGenerator_h + + +#include "cmCPackGenerator.h" + +class cmCPackComponent; + +/** \class cmCPackPKGGenerator + * \brief A generator for pkg files + * + */ +class cmCPackPKGGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackPKGGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackPKGGenerator(); + virtual ~cmCPackPKGGenerator(); + + virtual bool SupportsComponentInstallation() const; + +protected: + virtual int InitializeInternal(); + virtual const char* GetOutputPostfix() { return "darwin"; } + + // Copies or creates the resource file with the given name to the + // package or package staging directory dirName. The variable + // CPACK_RESOURCE_FILE_${NAME} (where ${NAME} is the uppercased + // version of name) specifies the input file to use for this file, + // which will be configured via ConfigureFile. + bool CopyCreateResourceFile(const std::string& name, + const std::string& dirName); + bool CopyResourcePlistFile(const std::string& name, const char* outName = 0); + + int CopyInstallScript(const std::string& resdir, const std::string& script, + const std::string& name); + + // Retrieve the name of package file that will be generated for this + // component. The name is just the file name with extension, and + // does not include the subdirectory. + std::string GetPackageName(const cmCPackComponent& component); + + // Writes a distribution.dist file, which turns a metapackage into a + // full-fledged distribution. This file is used to describe + // inter-component dependencies. metapackageFile is the name of the + // metapackage for the distribution. Only valid for a + // component-based install. + void WriteDistributionFile(const char* metapackageFile); + + // Subroutine of WriteDistributionFile that writes out the + // dependency attributes for inter-component dependencies. + void AddDependencyAttributes(const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, + std::ostringstream& out); + + // Subroutine of WriteDistributionFile that writes out the + // reverse dependency attributes for inter-component dependencies. + void AddReverseDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& out); + + // Generates XML that encodes the hierarchy of component groups and + // their components in a form that can be used by distribution + // metapackages. + void CreateChoiceOutline(const cmCPackComponentGroup& group, + std::ostringstream& out); + + /// Create the "choice" XML element to describe a component group + /// for the installer GUI. + void CreateChoice(const cmCPackComponentGroup& group, + std::ostringstream& out); + + /// Create the "choice" XML element to describe a component for the + /// installer GUI. + void CreateChoice(const cmCPackComponent& component, + std::ostringstream& out); + + // Escape the given string to make it usable as an XML attribute + // value. + std::string EscapeForXML(std::string str); + + // The PostFlight component when creating a metapackage + cmCPackComponent PostFlightComponent; + +}; + +#endif diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx index 5ca3ff7..5bb24c0 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.cxx +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -46,21 +46,6 @@ bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const return this->PackageCompatibilityVersion >= getVersion(10, 4); } -int cmCPackPackageMakerGenerator::CopyInstallScript(const std::string& resdir, - const std::string& script, - const std::string& name) -{ - std::string dst = resdir; - dst += "/"; - dst += name; - cmSystemTools::CopyFileAlways(script.c_str(), dst.c_str()); - cmSystemTools::SetPermissions(dst.c_str(), 0777); - cmCPackLogger(cmCPackLog::LOG_VERBOSE, "copy script : " - << script << "\ninto " << dst.c_str() << std::endl); - - return 1; -} - int cmCPackPackageMakerGenerator::PackageFiles() { // TODO: Use toplevel @@ -344,8 +329,6 @@ int cmCPackPackageMakerGenerator::PackageFiles() int cmCPackPackageMakerGenerator::InitializeInternal() { - cmCPackLogger(cmCPackLog::LOG_DEBUG, - "cmCPackPackageMakerGenerator::Initialize()" << std::endl); this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); // Starting with Xcode 4.3, PackageMaker is a separate app, and you @@ -481,80 +464,6 @@ int cmCPackPackageMakerGenerator::InitializeInternal() return this->Superclass::InitializeInternal(); } -bool cmCPackPackageMakerGenerator::CopyCreateResourceFile( - const std::string& name, const std::string& dirName) -{ - std::string uname = cmSystemTools::UpperCase(name); - std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname; - const char* inFileName = this->GetOption(cpackVar.c_str()); - if (!inFileName) { - cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " - << cpackVar.c_str() - << " not specified. It should point to " - << (!name.empty() ? name : "<empty>") << ".rtf, " << name - << ".html, or " << name << ".txt file" << std::endl); - return false; - } - if (!cmSystemTools::FileExists(inFileName)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find " - << (!name.empty() ? name : "<empty>") - << " resource file: " << inFileName << std::endl); - return false; - } - std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName); - if (ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt") { - cmCPackLogger( - cmCPackLog::LOG_ERROR, "Bad file extension specified: " - << ext - << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." - << std::endl); - return false; - } - - std::string destFileName = dirName; - destFileName += '/'; - destFileName += name + ext; - - // Set this so that distribution.dist gets the right name (without - // the path). - this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(), - (name + ext).c_str()); - - cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " - << (inFileName ? inFileName : "(NULL)") << " to " - << destFileName.c_str() << std::endl); - this->ConfigureFile(inFileName, destFileName.c_str()); - return true; -} - -bool cmCPackPackageMakerGenerator::CopyResourcePlistFile( - const std::string& name, const char* outName) -{ - if (!outName) { - outName = name.c_str(); - } - - std::string inFName = "CPack."; - inFName += name; - inFName += ".in"; - std::string inFileName = this->FindTemplate(inFName.c_str()); - if (inFileName.empty()) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Cannot find input file: " << inFName << std::endl); - return false; - } - - std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); - destFileName += "/"; - destFileName += outName; - - cmCPackLogger(cmCPackLog::LOG_VERBOSE, - "Configure file: " << inFileName.c_str() << " to " - << destFileName.c_str() << std::endl); - this->ConfigureFile(inFileName.c_str(), destFileName.c_str()); - return true; -} - bool cmCPackPackageMakerGenerator::RunPackageMaker(const char* command, const char* packageFile) { @@ -597,21 +506,6 @@ bool cmCPackPackageMakerGenerator::RunPackageMaker(const char* command, return true; } -std::string cmCPackPackageMakerGenerator::GetPackageName( - const cmCPackComponent& component) -{ - if (component.ArchiveFile.empty()) { - std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); - packagesDir += ".dummy"; - std::ostringstream out; - out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-" - << component.Name << ".pkg"; - return out.str(); - } else { - return component.ArchiveFile + ".pkg"; - } -} - bool cmCPackPackageMakerGenerator::GenerateComponentPackage( const char* packageFile, const char* packageDir, const cmCPackComponent& component) @@ -686,219 +580,3 @@ bool cmCPackPackageMakerGenerator::GenerateComponentPackage( // Run PackageMaker return RunPackageMaker(pkgCmd.str().c_str(), packageFile); } - -void cmCPackPackageMakerGenerator::WriteDistributionFile( - const char* metapackageFile) -{ - std::string distributionTemplate = - this->FindTemplate("CPack.distribution.dist.in"); - if (distributionTemplate.empty()) { - cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: " - << distributionTemplate << std::endl); - return; - } - - std::string distributionFile = metapackageFile; - distributionFile += "/Contents/distribution.dist"; - - // Create the choice outline, which provides a tree-based view of - // the components in their groups. - std::ostringstream choiceOut; - choiceOut << "<choices-outline>" << std::endl; - - // Emit the outline for the groups - std::map<std::string, cmCPackComponentGroup>::iterator groupIt; - for (groupIt = this->ComponentGroups.begin(); - groupIt != this->ComponentGroups.end(); ++groupIt) { - if (groupIt->second.ParentGroup == 0) { - CreateChoiceOutline(groupIt->second, choiceOut); - } - } - - // Emit the outline for the non-grouped components - std::map<std::string, cmCPackComponent>::iterator compIt; - for (compIt = this->Components.begin(); compIt != this->Components.end(); - ++compIt) { - if (!compIt->second.Group) { - choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>" - << std::endl; - } - } - if (!this->PostFlightComponent.Name.empty()) { - choiceOut << "<line choice=\"" << PostFlightComponent.Name - << "Choice\"></line>" << std::endl; - } - choiceOut << "</choices-outline>" << std::endl; - - // Create the actual choices - for (groupIt = this->ComponentGroups.begin(); - groupIt != this->ComponentGroups.end(); ++groupIt) { - CreateChoice(groupIt->second, choiceOut); - } - for (compIt = this->Components.begin(); compIt != this->Components.end(); - ++compIt) { - CreateChoice(compIt->second, choiceOut); - } - - if (!this->PostFlightComponent.Name.empty()) { - CreateChoice(PostFlightComponent, choiceOut); - } - - this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str()); - - // Create the distribution.dist file in the metapackage to turn it - // into a distribution package. - this->ConfigureFile(distributionTemplate.c_str(), distributionFile.c_str()); -} - -void cmCPackPackageMakerGenerator::CreateChoiceOutline( - const cmCPackComponentGroup& group, std::ostringstream& out) -{ - out << "<line choice=\"" << group.Name << "Choice\">" << std::endl; - std::vector<cmCPackComponentGroup*>::const_iterator groupIt; - for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end(); - ++groupIt) { - CreateChoiceOutline(**groupIt, out); - } - - std::vector<cmCPackComponent*>::const_iterator compIt; - for (compIt = group.Components.begin(); compIt != group.Components.end(); - ++compIt) { - out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>" - << std::endl; - } - out << "</line>" << std::endl; -} - -void cmCPackPackageMakerGenerator::CreateChoice( - const cmCPackComponentGroup& group, std::ostringstream& out) -{ - out << "<choice id=\"" << group.Name << "Choice\" " - << "title=\"" << group.DisplayName << "\" " - << "start_selected=\"true\" " - << "start_enabled=\"true\" " - << "start_visible=\"true\" "; - if (!group.Description.empty()) { - out << "description=\"" << EscapeForXML(group.Description) << "\""; - } - out << "></choice>" << std::endl; -} - -void cmCPackPackageMakerGenerator::CreateChoice( - const cmCPackComponent& component, std::ostringstream& out) -{ - std::string packageId = "com."; - packageId += this->GetOption("CPACK_PACKAGE_VENDOR"); - packageId += '.'; - packageId += this->GetOption("CPACK_PACKAGE_NAME"); - packageId += '.'; - packageId += component.Name; - - out << "<choice id=\"" << component.Name << "Choice\" " - << "title=\"" << component.DisplayName << "\" " - << "start_selected=\"" - << (component.IsDisabledByDefault && !component.IsRequired ? "false" - : "true") - << "\" " - << "start_enabled=\"" << (component.IsRequired ? "false" : "true") - << "\" " - << "start_visible=\"" << (component.IsHidden ? "false" : "true") - << "\" "; - if (!component.Description.empty()) { - out << "description=\"" << EscapeForXML(component.Description) << "\" "; - } - if (!component.Dependencies.empty() || - !component.ReverseDependencies.empty()) { - // The "selected" expression is evaluated each time any choice is - // selected, for all choices *except* the one that the user - // selected. A component is marked selected if it has been - // selected (my.choice.selected in Javascript) and all of the - // components it depends on have been selected (transitively) or - // if any of the components that depend on it have been selected - // (transitively). Assume that we have components A, B, C, D, and - // E, where each component depends on the previous component (B - // depends on A, C depends on B, D depends on C, and E depends on - // D). The expression we build for the component C will be - // my.choice.selected && B && A || D || E - // This way, selecting C will automatically select everything it depends - // on (B and A), while selecting something that depends on C--either D - // or E--will automatically cause C to get selected. - out << "selected=\"my.choice.selected"; - std::set<const cmCPackComponent*> visited; - AddDependencyAttributes(component, visited, out); - visited.clear(); - AddReverseDependencyAttributes(component, visited, out); - out << "\""; - } - out << ">" << std::endl; - out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl; - out << "</choice>" << std::endl; - - // Create a description of the package associated with this - // component. - std::string relativePackageLocation = "Contents/Packages/"; - relativePackageLocation += this->GetPackageName(component); - - // Determine the installed size of the package. - std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); - dirName += '/'; - dirName += component.Name; - dirName += this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); - unsigned long installedSize = - component.GetInstalledSizeInKbytes(dirName.c_str()); - - out << "<pkg-ref id=\"" << packageId << "\" " - << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" " - << "installKBytes=\"" << installedSize << "\" " - << "auth=\"Admin\" onConclusion=\"None\">"; - if (component.IsDownloaded) { - out << this->GetOption("CPACK_DOWNLOAD_SITE") - << this->GetPackageName(component); - } else { - out << "file:./" << relativePackageLocation; - } - out << "</pkg-ref>" << std::endl; -} - -void cmCPackPackageMakerGenerator::AddDependencyAttributes( - const cmCPackComponent& component, - std::set<const cmCPackComponent*>& visited, std::ostringstream& out) -{ - if (visited.find(&component) != visited.end()) { - return; - } - visited.insert(&component); - - std::vector<cmCPackComponent*>::const_iterator dependIt; - for (dependIt = component.Dependencies.begin(); - dependIt != component.Dependencies.end(); ++dependIt) { - out << " && choices['" << (*dependIt)->Name << "Choice'].selected"; - AddDependencyAttributes(**dependIt, visited, out); - } -} - -void cmCPackPackageMakerGenerator::AddReverseDependencyAttributes( - const cmCPackComponent& component, - std::set<const cmCPackComponent*>& visited, std::ostringstream& out) -{ - if (visited.find(&component) != visited.end()) { - return; - } - visited.insert(&component); - - std::vector<cmCPackComponent*>::const_iterator dependIt; - for (dependIt = component.ReverseDependencies.begin(); - dependIt != component.ReverseDependencies.end(); ++dependIt) { - out << " || choices['" << (*dependIt)->Name << "Choice'].selected"; - AddReverseDependencyAttributes(**dependIt, visited, out); - } -} - -std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str) -{ - cmSystemTools::ReplaceString(str, "&", "&"); - cmSystemTools::ReplaceString(str, "<", "<"); - cmSystemTools::ReplaceString(str, ">", ">"); - cmSystemTools::ReplaceString(str, "\"", """); - return str; -} diff --git a/Source/CPack/cmCPackPackageMakerGenerator.h b/Source/CPack/cmCPackPackageMakerGenerator.h index 2b8d253..5192377 100644 --- a/Source/CPack/cmCPackPackageMakerGenerator.h +++ b/Source/CPack/cmCPackPackageMakerGenerator.h @@ -13,7 +13,7 @@ #ifndef cmCPackPackageMakerGenerator_h #define cmCPackPackageMakerGenerator_h -#include "cmCPackGenerator.h" +#include "cmCPackPKGGenerator.h" class cmCPackComponent; @@ -23,46 +23,28 @@ class cmCPackComponent; * http://developer.apple.com/documentation/Darwin * /Reference/ManPages/man1/packagemaker.1.html */ -class cmCPackPackageMakerGenerator : public cmCPackGenerator +class cmCPackPackageMakerGenerator : public cmCPackPKGGenerator { public: - cmCPackTypeMacro(cmCPackPackageMakerGenerator, cmCPackGenerator); + cmCPackTypeMacro(cmCPackPackageMakerGenerator, cmCPackPKGGenerator); /** * Construct generator */ cmCPackPackageMakerGenerator(); virtual ~cmCPackPackageMakerGenerator(); - - virtual bool SupportsComponentInstallation() const; + bool SupportsComponentInstallation() const; protected: - int CopyInstallScript(const std::string& resdir, const std::string& script, - const std::string& name); virtual int InitializeInternal(); int PackageFiles(); virtual const char* GetOutputExtension() { return ".dmg"; } - virtual const char* GetOutputPostfix() { return "darwin"; } - - // Copies or creates the resource file with the given name to the - // package or package staging directory dirName. The variable - // CPACK_RESOURCE_FILE_${NAME} (where ${NAME} is the uppercased - // version of name) specifies the input file to use for this file, - // which will be configured via ConfigureFile. - bool CopyCreateResourceFile(const std::string& name, - const std::string& dirName); - bool CopyResourcePlistFile(const std::string& name, const char* outName = 0); // Run PackageMaker with the given command line, which will (if // successful) produce the given package file. Returns true if // PackageMaker succeeds, false otherwise. bool RunPackageMaker(const char* command, const char* packageFile); - // Retrieve the name of package file that will be generated for this - // component. The name is just the file name with extension, and - // does not include the subdirectory. - std::string GetPackageName(const cmCPackComponent& component); - // Generate a package in the file packageFile for the given // component. All of the files within this component are stored in // the directory packageDir. Returns true if successful, false @@ -71,48 +53,6 @@ protected: const char* packageDir, const cmCPackComponent& component); - // Writes a distribution.dist file, which turns a metapackage into a - // full-fledged distribution. This file is used to describe - // inter-component dependencies. metapackageFile is the name of the - // metapackage for the distribution. Only valid for a - // component-based install. - void WriteDistributionFile(const char* metapackageFile); - - // Subroutine of WriteDistributionFile that writes out the - // dependency attributes for inter-component dependencies. - void AddDependencyAttributes(const cmCPackComponent& component, - std::set<const cmCPackComponent*>& visited, - std::ostringstream& out); - - // Subroutine of WriteDistributionFile that writes out the - // reverse dependency attributes for inter-component dependencies. - void AddReverseDependencyAttributes( - const cmCPackComponent& component, - std::set<const cmCPackComponent*>& visited, std::ostringstream& out); - - // Generates XML that encodes the hierarchy of component groups and - // their components in a form that can be used by distribution - // metapackages. - void CreateChoiceOutline(const cmCPackComponentGroup& group, - std::ostringstream& out); - - /// Create the "choice" XML element to describe a component group - /// for the installer GUI. - void CreateChoice(const cmCPackComponentGroup& group, - std::ostringstream& out); - - /// Create the "choice" XML element to describe a component for the - /// installer GUI. - void CreateChoice(const cmCPackComponent& component, - std::ostringstream& out); - - // Escape the given string to make it usable as an XML attribute - // value. - std::string EscapeForXML(std::string str); - - // The PostFlight component when creating a metapackage - cmCPackComponent PostFlightComponent; - double PackageMakerVersion; unsigned int PackageCompatibilityVersion; }; diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx new file mode 100644 index 0000000..c1f63cc --- /dev/null +++ b/Source/CPack/cmCPackProductBuildGenerator.cxx @@ -0,0 +1,260 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackProductBuildGenerator.h" + +#include "cmake.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmSystemTools.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" + +#include <cmsys/SystemTools.hxx> +#include <cmsys/Glob.hxx> + +cmCPackProductBuildGenerator::cmCPackProductBuildGenerator() +{ + this->componentPackageMethod = ONE_PACKAGE; +} + +cmCPackProductBuildGenerator::~cmCPackProductBuildGenerator() +{ +} + +int cmCPackProductBuildGenerator::PackageFiles() +{ + // TODO: Use toplevel + // It is used! Is this an obsolete comment? + + std::string packageDirFileName + = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + + // Create the directory where component packages will be built. + std::string basePackageDir = packageDirFileName; + basePackageDir += "/Contents/Packages"; + if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating component packages directory: " + << basePackageDir.c_str() << std::endl); + return 0; + } + + if (!this->Components.empty()) + { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) + { + std::string packageDir = toplevel; + packageDir += '/'; + packageDir += compIt->first; + if (!this->GenerateComponentPackage(basePackageDir, + GetPackageName(compIt->second), + packageDir, + &compIt->second)) + { + return 0; + } + } + } + else + { + if(!this->GenerateComponentPackage(basePackageDir, + this->GetOption("CPACK_PACKAGE_NAME"), + toplevel, NULL)) + { + return 0; + } + } + + // Copy or create all of the resource files we need. + std::string resDir = packageDirFileName + "/Contents"; + if ( !this->CopyCreateResourceFile("License", resDir.c_str()) + || !this->CopyCreateResourceFile("ReadMe", resDir.c_str()) + || !this->CopyCreateResourceFile("Welcome", resDir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files" + << std::endl); + return 0; + } + + // combine package(s) into a distribution + WriteDistributionFile(packageDirFileName.c_str()); + std::ostringstream pkgCmd; + + std::string version = this->GetOption("CPACK_PACKAGE_VERSION"); + std::string productbuild = this->GetOption("CPACK_COMMAND_PRODUCTBUILD"); + + pkgCmd << productbuild + << " --distribution \"" << packageDirFileName + << "/Contents/distribution.dist\"" + << " --package-path \"" << packageDirFileName << "/Contents/Packages" << "\"" + << " --resources \"" << resDir << "\"" + << " --version \"" << version << "\"" + << " \"" << packageFileNames[0] << "\""; + + // Run ProductBuild + return RunProductBuild(pkgCmd.str()); +} + +int cmCPackProductBuildGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/Applications"); + + std::vector<std::string> no_paths; + std::string program = + cmSystemTools::FindProgram("pkgbuild", no_paths, false); + if (program.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find pkgbuild executable" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_PKGBUILD", program.c_str()); + + + program = cmSystemTools::FindProgram("productbuild", no_paths, false); + if (program.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find productbuild executable" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_PRODUCTBUILD", program.c_str()); + + return this->Superclass::InitializeInternal(); +} + + +bool cmCPackProductBuildGenerator::RunProductBuild( + const std::string& command) +{ + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/ProductBuildOutput.log"; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl); + std::string output, error_output; + int retVal = 1; + bool res = cmSystemTools::RunSingleCommand(command.c_str(), + &output, &error_output, &retVal, 0, this->GeneratorVerbose, 0); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running command" + << std::endl); + if ( !res || retVal ) + { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << command << std::endl + << "# Output:" << std::endl + << output.c_str() << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem running command: " << command + << std::endl << "Please check " << tmpFile.c_str() << " for errors" + << std::endl); + return false; + } + return true; +} + +bool cmCPackProductBuildGenerator::GenerateComponentPackage( + const std::string& packageFileDir, + const std::string& packageFileName, + const std::string& packageDir, + const cmCPackComponent* component) +{ + std::string packageFile = packageFileDir; + packageFile += '/'; + packageFile += packageFileName; + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Building component package: " << + packageFile << std::endl); + + const char* comp_name = component ? component->Name.c_str() : NULL; + + const char* preflight = this->GetComponentScript("PREFLIGHT", comp_name); + const char* postflight = this->GetComponentScript("POSTFLIGHT", comp_name); + + std::string resDir = packageFileDir; + if(component) + { + resDir += "/"; + resDir += component->Name; + } + std::string scriptDir = resDir + "/scripts"; + + if ( !cmsys::SystemTools::MakeDirectory(scriptDir.c_str())) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating installer directory: " + << scriptDir.c_str() << std::endl); + return 0; + } + + // if preflight, postflight, or postupgrade are set + // then copy them into the script directory and make + // them executable + if(preflight) + { + this->CopyInstallScript(scriptDir.c_str(), + preflight, + "preinstall"); + } + if(postflight) + { + this->CopyInstallScript(scriptDir.c_str(), + postflight, + "postinstall"); + } + + + // The command that will be used to run ProductBuild + std::ostringstream pkgCmd; + + std::string pkgId = "com."; + pkgId += this->GetOption("CPACK_PACKAGE_VENDOR"); + pkgId += '.'; + pkgId += this->GetOption("CPACK_PACKAGE_NAME"); + if(component) + { + pkgId += '.'; + pkgId += component->Name; + } + + std::string version = this->GetOption("CPACK_PACKAGE_VERSION"); + std::string pkgbuild = this->GetOption("CPACK_COMMAND_PKGBUILD"); + + pkgCmd << pkgbuild + << " --root \"" << packageDir << "\"" + << " --identifier \"" << pkgId << "\"" + << " --scripts \"" << scriptDir << "\"" + << " --version \"" << version << "\"" + << " --install-location \"/\"" + << " \"" << packageFile << "\""; + + // Run ProductBuild + return RunProductBuild(pkgCmd.str()); +} + +const char* cmCPackProductBuildGenerator::GetComponentScript( + const char* script, + const char* component_name) +{ + std::string scriptname = std::string("CPACK_") + script + "_"; + if(component_name) + { + scriptname += cmSystemTools::UpperCase(component_name); + scriptname += "_"; + } + scriptname += "SCRIPT"; + + return this->GetOption(scriptname); +} diff --git a/Source/CPack/cmCPackProductBuildGenerator.h b/Source/CPack/cmCPackProductBuildGenerator.h new file mode 100644 index 0000000..0740f89 --- /dev/null +++ b/Source/CPack/cmCPackProductBuildGenerator.h @@ -0,0 +1,60 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackProductBuildGenerator_h +#define cmCPackProductBuildGenerator_h + + +#include "cmCPackPKGGenerator.h" + +class cmCPackComponent; + +/** \class cmCPackProductBuildGenerator + * \brief A generator for ProductBuild files + * + */ +class cmCPackProductBuildGenerator : public cmCPackPKGGenerator +{ +public: + cmCPackTypeMacro(cmCPackProductBuildGenerator, cmCPackPKGGenerator); + + /** + * Construct generator + */ + cmCPackProductBuildGenerator(); + virtual ~cmCPackProductBuildGenerator(); + +protected: + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetOutputExtension() { return ".pkg"; } + + // Run ProductBuild with the given command line, which will (if + // successful) produce the given package file. Returns true if + // ProductBuild succeeds, false otherwise. + bool RunProductBuild(const std::string& command); + + // Generate a package in the file packageFile for the given + // component. All of the files within this component are stored in + // the directory packageDir. Returns true if successful, false + // otherwise. + bool GenerateComponentPackage(const std::string& packageFileDir, + const std::string& packageFileName, + const std::string& packageDir, + const cmCPackComponent* component); + + const char* GetComponentScript(const char* script, + const char* script_component); + +}; + +#endif |