diff options
Diffstat (limited to 'Source/CPack/cmCPackPKGGenerator.cxx')
-rw-r--r-- | Source/CPack/cmCPackPKGGenerator.cxx | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx new file mode 100644 index 0000000..8c22c65 --- /dev/null +++ b/Source/CPack/cmCPackPKGGenerator.cxx @@ -0,0 +1,342 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCPackPKGGenerator.h" + +#include <vector> + +#include "cmCPackComponentGroup.h" +#include "cmCPackGenerator.h" +#include "cmCPackLog.h" +#include "cmSystemTools.h" +#include "cmXMLWriter.h" + +cmCPackPKGGenerator::cmCPackPKGGenerator() +{ + this->componentPackageMethod = ONE_PACKAGE; +} + +cmCPackPKGGenerator::~cmCPackPKGGenerator() = default; + +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(); + } + + 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; + cmXMLWriter xout(choiceOut, 1); + xout.StartElement("choices-outline"); + + // Emit the outline for the groups + for (auto const& group : this->ComponentGroups) { + if (group.second.ParentGroup == nullptr) { + CreateChoiceOutline(group.second, xout); + } + } + + // Emit the outline for the non-grouped components + for (auto const& comp : this->Components) { + if (!comp.second.Group) { + xout.StartElement("line"); + xout.Attribute("choice", comp.first + "Choice"); + xout.Content(""); // Avoid self-closing tag. + xout.EndElement(); + } + } + if (!this->PostFlightComponent.Name.empty()) { + xout.StartElement("line"); + xout.Attribute("choice", PostFlightComponent.Name + "Choice"); + xout.Content(""); // Avoid self-closing tag. + xout.EndElement(); + } + xout.EndElement(); // choices-outline> + + // Create the actual choices + for (auto const& group : this->ComponentGroups) { + CreateChoice(group.second, xout); + } + for (auto const& comp : this->Components) { + CreateChoice(comp.second, xout); + } + + if (!this->PostFlightComponent.Name.empty()) { + CreateChoice(PostFlightComponent, xout); + } + + 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, distributionFile); +} + +void cmCPackPKGGenerator::CreateChoiceOutline( + const cmCPackComponentGroup& group, cmXMLWriter& xout) +{ + xout.StartElement("line"); + xout.Attribute("choice", group.Name + "Choice"); + for (cmCPackComponentGroup* subgroup : group.Subgroups) { + CreateChoiceOutline(*subgroup, xout); + } + + for (cmCPackComponent* comp : group.Components) { + xout.StartElement("line"); + xout.Attribute("choice", comp->Name + "Choice"); + xout.Content(""); // Avoid self-closing tag. + xout.EndElement(); + } + xout.EndElement(); +} + +void cmCPackPKGGenerator::CreateChoice(const cmCPackComponentGroup& group, + cmXMLWriter& xout) +{ + xout.StartElement("choice"); + xout.Attribute("id", group.Name + "Choice"); + xout.Attribute("title", group.DisplayName); + xout.Attribute("start_selected", "true"); + xout.Attribute("start_enabled", "true"); + xout.Attribute("start_visible", "true"); + if (!group.Description.empty()) { + xout.Attribute("description", group.Description); + } + xout.EndElement(); +} + +void cmCPackPKGGenerator::CreateChoice(const cmCPackComponent& component, + cmXMLWriter& xout) +{ + std::string packageId = "com."; + packageId += this->GetOption("CPACK_PACKAGE_VENDOR"); + packageId += '.'; + packageId += this->GetOption("CPACK_PACKAGE_NAME"); + packageId += '.'; + packageId += component.Name; + + xout.StartElement("choice"); + xout.Attribute("id", component.Name + "Choice"); + xout.Attribute("title", component.DisplayName); + xout.Attribute( + "start_selected", + component.IsDisabledByDefault && !component.IsRequired ? "false" : "true"); + xout.Attribute("start_enabled", component.IsRequired ? "false" : "true"); + xout.Attribute("start_visible", component.IsHidden ? "false" : "true"); + if (!component.Description.empty()) { + xout.Attribute("description", 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. + std::ostringstream selected("my.choice.selected", std::ios_base::ate); + std::set<const cmCPackComponent*> visited; + AddDependencyAttributes(component, visited, selected); + visited.clear(); + AddReverseDependencyAttributes(component, visited, selected); + xout.Attribute("selected", selected.str()); + } + xout.StartElement("pkg-ref"); + xout.Attribute("id", packageId); + xout.EndElement(); // pkg-ref + xout.EndElement(); // choice + + // 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); + + xout.StartElement("pkg-ref"); + xout.Attribute("id", packageId); + xout.Attribute("version", this->GetOption("CPACK_PACKAGE_VERSION")); + xout.Attribute("installKBytes", installedSize); + xout.Attribute("auth", "Admin"); + xout.Attribute("onConclusion", "None"); + if (component.IsDownloaded) { + xout.Content(this->GetOption("CPACK_DOWNLOAD_SITE")); + xout.Content(this->GetPackageName(component)); + } else { + xout.Content("file:./"); + xout.Content(cmSystemTools::EncodeURL(relativePackageLocation, + /*escapeSlashes=*/false)); + } + xout.EndElement(); // pkg-ref +} + +void cmCPackPKGGenerator::AddDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& out) +{ + if (visited.find(&component) != visited.end()) { + return; + } + visited.insert(&component); + + for (cmCPackComponent* depend : component.Dependencies) { + out << " && choices['" << depend->Name << "Choice'].selected"; + AddDependencyAttributes(*depend, 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); + + for (cmCPackComponent* depend : component.ReverseDependencies) { + out << " || choices['" << depend->Name << "Choice'].selected"; + AddReverseDependencyAttributes(*depend, visited, out); + } +} + +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); + 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", + (name + ext).c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Configure file: " << (inFileName ? inFileName : "(NULL)") + << " to " << destFileName << std::endl); + this->ConfigureFile(inFileName, destFileName); + 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 << " to " << destFileName + << std::endl); + this->ConfigureFile(inFileName, destFileName); + 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, dst); + cmSystemTools::SetPermissions(dst.c_str(), 0777); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "copy script : " << script << "\ninto " << dst << std::endl); + + return 1; +} |