| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- /* 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 "cmStringAlgorithms.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 =
- cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy");
- std::ostringstream out;
- out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-"
- << component.Name << ".pkg";
- return out.str();
- }
- return component.ArchiveFile + ".pkg";
- }
- void cmCPackPKGGenerator::CreateBackground(const char* themeName,
- const char* metapackageFile,
- cm::string_view genName,
- cmXMLWriter& xout)
- {
- std::string paramSuffix =
- (themeName == nullptr) ? "" : cmSystemTools::UpperCase(themeName);
- std::string opt = (themeName == nullptr)
- ? cmStrCat("CPACK_", genName, "_BACKGROUND")
- : cmStrCat("CPACK_", genName, "_BACKGROUND_", paramSuffix);
- const char* bgFileName = this->GetOption(opt);
- if (bgFileName == nullptr) {
- return;
- }
- std::string bgFilePath = cmStrCat(metapackageFile, "/Contents/", bgFileName);
- if (!cmSystemTools::FileExists(bgFilePath)) {
- cmCPackLogger(cmCPackLog::LOG_ERROR,
- "Background image doesn't exist in the resource directory: "
- << bgFileName << std::endl);
- return;
- }
- if (themeName == nullptr) {
- xout.StartElement("background");
- } else {
- xout.StartElement(cmStrCat("background-", themeName));
- }
- xout.Attribute("file", bgFileName);
- const char* param = this->GetOption(cmStrCat(opt, "_ALIGNMENT"));
- if (param != nullptr) {
- xout.Attribute("alignment", param);
- }
- param = this->GetOption(cmStrCat(opt, "_SCALING"));
- if (param != nullptr) {
- xout.Attribute("scaling", param);
- }
- // Apple docs say that you must provide either mime-type or uti
- // attribute for the background, but I've seen examples that
- // doesn't have them, so don't make them mandatory.
- param = this->GetOption(cmStrCat(opt, "_MIME_TYPE"));
- if (param != nullptr) {
- xout.Attribute("mime-type", param);
- }
- param = this->GetOption(cmStrCat(opt, "_UTI"));
- if (param != nullptr) {
- xout.Attribute("uti", param);
- }
- xout.EndElement();
- }
- void cmCPackPKGGenerator::WriteDistributionFile(const char* metapackageFile,
- const char* genName)
- {
- std::string distributionTemplate =
- this->FindTemplate("Internal/CPack/CPack.distribution.dist.in");
- if (distributionTemplate.empty()) {
- cmCPackLogger(cmCPackLog::LOG_ERROR,
- "Cannot find input file: " << distributionTemplate
- << std::endl);
- return;
- }
- std::string distributionFile =
- cmStrCat(metapackageFile, "/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);
- }
- // default background
- this->CreateBackground(nullptr, metapackageFile, genName, xout);
- // Dark Aqua
- this->CreateBackground("darkAqua", metapackageFile, genName, 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 =
- cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.',
- this->GetOption("CPACK_PACKAGE_NAME"), '.', 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 =
- cmStrCat("Contents/Packages/", this->GetPackageName(component));
- // Determine the installed size of the package.
- std::string dirName =
- cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), '/', component.Name,
- 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 = cmStrCat(dirName, '/', 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 = cmStrCat("Internal/CPack/CPack.", name, ".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 =
- cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/', 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 = cmStrCat(resdir, '/', name);
- cmSystemTools::CopyFileAlways(script, dst);
- cmSystemTools::SetPermissions(dst.c_str(), 0777);
- cmCPackLogger(cmCPackLog::LOG_VERBOSE,
- "copy script : " << script << "\ninto " << dst << std::endl);
- return 1;
- }
|