summaryrefslogtreecommitdiffstats
path: root/Source/CPack/cmCPackPackageMakerGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CPack/cmCPackPackageMakerGenerator.cxx')
-rw-r--r--Source/CPack/cmCPackPackageMakerGenerator.cxx518
1 files changed, 461 insertions, 57 deletions
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx
index a5884a9..e4a003d 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.cxx
+++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx
@@ -22,6 +22,7 @@
#include "cmSystemTools.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
+#include "cmCPackComponentGroup.h"
#include "cmCPackLog.h"
#include <cmsys/SystemTools.hxx>
@@ -38,6 +39,13 @@ cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
{
}
+//----------------------------------------------------------------------
+bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
+{
+ return true;
+}
+
+//----------------------------------------------------------------------
int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
const char* script,
const char* name)
@@ -96,23 +104,59 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
// them executable
if(preflight)
{
- this->CopyInstallScript(resDir.c_str(),
- preflight,
- "preflight");
+ this->CopyInstallScript(resDir.c_str(),
+ preflight,
+ "preflight");
}
if(postflight)
{
- this->CopyInstallScript(resDir.c_str(),
- postflight,
- "postflight");
+ this->CopyInstallScript(resDir.c_str(),
+ postflight,
+ "postflight");
}
if(postupgrade)
{
- this->CopyInstallScript(resDir.c_str(),
- postupgrade,
- "postupgrade");
+ this->CopyInstallScript(resDir.c_str(),
+ postupgrade,
+ "postupgrade");
}
+ if (!this->Components.empty())
+ {
+ // Create the directory where component packages will be installed.
+ std::string basePackageDir = toplevel;
+ basePackageDir += "/packages";
+ if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem creating component packages directory: "
+ << basePackageDir.c_str() << std::endl);
+ return 0;
+ }
+
+ // Create packages for each component
+ std::map<std::string, cmCPackComponent>::iterator compIt;
+ for (compIt = this->Components.begin(); compIt != this->Components.end();
+ ++compIt)
+ {
+ std::string packageFile = basePackageDir;
+ packageFile += '/';
+ packageFile += GetPackageName(compIt->second);
+
+ std::string packageDir = toplevel;
+ packageDir += '/';
+ packageDir += compIt->first;
+ if (!this->GenerateComponentPackage(packageFile.c_str(),
+ packageDir.c_str(),
+ compIt->second))
+ {
+ return 0;
+ }
+ }
+ }
+ this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
+
+ // Copy or create all of the resource files we need.
if ( !this->CopyCreateResourceFile("License")
|| !this->CopyCreateResourceFile("ReadMe")
|| !this->CopyCreateResourceFile("Welcome")
@@ -126,68 +170,58 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
std::string packageDirFileName
= this->GetOption("CPACK_TEMPORARY_DIRECTORY");
- packageDirFileName += ".pkg";
+ if (this->Components.empty())
+ {
+ packageDirFileName += ".pkg";
+ }
+ else
+ {
+ packageDirFileName += ".mpkg";
+ if (this->PackageMakerVersion == 3.0)
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "PackageMaker 3.0 cannot build component-based installations."
+ << std::endl << "Please use PackageMaker 2.5 instead." << std::endl);
+ }
+ }
- std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
- tmpFile += "/PackageMakerOutput.log";
cmOStringStream pkgCmd;
pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
- << "\" -build -p \"" << packageDirFileName << "\" -f \""
- << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
- << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
- << "/Resources\" -i \""
- << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
- << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"";
- if ( this->PackageMakerVersion > 2.0 )
+ << "\" -build -p \"" << packageDirFileName << "\"";
+ if (this->Components.empty())
{
- pkgCmd << " -v";
+ pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
}
- cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << pkgCmd.str().c_str()
- << std::endl);
- std::string output;
- int retVal = 1;
- //bool res = cmSystemTools::RunSingleCommand(pkgCmd.str().c_str(), &output,
- //&retVal, 0, this->GeneratorVerbose, 0);
- bool res = true;
- retVal = system(pkgCmd.str().c_str());
- cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
- << std::endl);
- if ( !res || retVal )
+ else
{
- cmGeneratedFileStream ofs(tmpFile.c_str());
- ofs << "# Run command: " << pkgCmd.str().c_str() << std::endl
- << "# Output:" << std::endl
- << output.c_str() << std::endl;
- cmCPackLogger(cmCPackLog::LOG_ERROR,
- "Problem running PackageMaker command: " << pkgCmd.str().c_str()
- << std::endl << "Please check " << tmpFile.c_str() << " for errors"
- << std::endl);
- return 0;
+ pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
+ << "/packages/";
}
- // sometimes the pkgCmd finishes but the directory is not yet
- // created, so try 10 times to see if it shows up
- int tries = 10;
- while(tries > 0 &&
- !cmSystemTools::FileExists(packageDirFileName.c_str()))
+ pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
+ << "/Resources\" -i \""
+ << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
+ << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"";
+ if ( this->PackageMakerVersion > 2.0 )
{
- cmSystemTools::Delay(500);
- tries--;
+ pkgCmd << " -v";
}
- if(!cmSystemTools::FileExists(packageDirFileName.c_str()))
+ if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
+ return 0;
+
+ if (!this->Components.empty())
{
- cmCPackLogger(
- cmCPackLog::LOG_ERROR,
- "Problem running PackageMaker command: " << pkgCmd.str().c_str()
- << std::endl << "Package not created: " << packageDirFileName.c_str()
- << std::endl);
+ WriteDistributionFile(packageDirFileName.c_str());
}
- tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+
+ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/hdiutilOutput.log";
cmOStringStream dmgCmd;
dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
<< "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
<< "\" \"" << outFileName << "\"";
- res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
+ std::string output;
+ int retVal = 1;
+ bool res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
&retVal, 0, this->GeneratorVerbose, 0);
if ( !res || retVal )
{
@@ -339,8 +373,14 @@ bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name)
return true;
}
-bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
+bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
+ const char* outName)
{
+ if (!outName)
+ {
+ outName = name;
+ }
+
std::string inFName = "CPack.";
inFName += name;
inFName += ".in";
@@ -354,10 +394,374 @@ bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
destFileName += "/";
- destFileName += name;
+ 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)
+{
+ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ tmpFile += "/PackageMakerOutput.log";
+
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
+ std::string output;
+ int retVal = 1;
+ bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0,
+ this->GeneratorVerbose, 0);
+ cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
+ << 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 PackageMaker command: " << command
+ << std::endl << "Please check " << tmpFile.c_str() << " for errors"
+ << std::endl);
+ return false;
+ }
+ // sometimes the command finishes but the directory is not yet
+ // created, so try 10 times to see if it shows up
+ int tries = 10;
+ while(tries > 0 &&
+ !cmSystemTools::FileExists(packageFile))
+ {
+ cmSystemTools::Delay(500);
+ tries--;
+ }
+ if(!cmSystemTools::FileExists(packageFile))
+ {
+ cmCPackLogger(
+ cmCPackLog::LOG_ERROR,
+ "Problem running PackageMaker command: " << command
+ << std::endl << "Package not created: " << packageFile
+ << std::endl);
+ return false;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+std::string
+cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component)
+{
+ std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ packagesDir += ".dummy";
+ cmOStringStream out;
+ out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
+ << "-" << component.Name << ".pkg";
+ return out.str();
+}
+
+//----------------------------------------------------------------------
+bool
+cmCPackPackageMakerGenerator::
+GenerateComponentPackage(const char *packageFile,
+ const char *packageDir,
+ const cmCPackComponent& component)
+{
+ cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+ "- Building component package: " << packageFile << std::endl);
+
+ // Create the description file for this component.
+ std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ descriptionFile += '/' + component.Name + "-Description.plist";
+ std::ofstream out(descriptionFile.c_str());
+ out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
+ << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
+ << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl
+ << "<plist version=\"1.4\">" << std::endl
+ << "<dict>" << std::endl
+ << " <key>IFPkgDescriptionTitle</key>" << std::endl
+ << " <string>" << component.DisplayName << "</string>" << std::endl
+ << " <key>IFPkgDescriptionVersion</key>" << std::endl
+ << " <string>" << this->GetOption("CPACK_PACKAGE_VERSION")
+ << "</string>" << std::endl
+ << " <key>IFPkgDescriptionDescription</key>" << std::endl
+ << " <string>" + this->EscapeForXML(component.Description)
+ << "</string>" << std::endl
+ << "</dict>" << std::endl
+ << "</plist>" << std::endl;
+ out.close();
+
+ // Create the Info.plist file for this component
+ std::string moduleVersionSuffix = ".";
+ moduleVersionSuffix += component.Name;
+ this->SetOption("CPACK_MODULE_VERSION_SUFFIX", moduleVersionSuffix.c_str());
+ std::string infoFileName = component.Name;
+ infoFileName += "-Info.plist";
+ if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str()))
+ {
+ return false;
+ }
+
+ // Run PackageMaker
+ cmOStringStream pkgCmd;
+ pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
+ << "\" -build -p \"" << packageFile << "\""
+ << " -f \"" << packageDir << "\""
+ << "-i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
+ << "/" << infoFileName << "\""
+ << "-d \"" << descriptionFile << "\"";
+ 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.
+ cmOStringStream 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)
+ {
+ 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;
+ }
+ }
+ 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);
+ }
+ 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, cmOStringStream& out)
+{
+ out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
+ 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,
+ cmOStringStream& 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,
+ cmOStringStream& out)
+{
+ std::string packageId = "com.";
+ packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
+ packageId += '.';
+ packageId += this->GetOption("CPACK_PACKAGE_NAME");
+ packageId += '.';
+ packageId += this->GetOption("CPACK_PACKAGE_VERSION");
+ 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";
+ AddDependencyAttributes(component, out);
+ AddReverseDependencyAttributes(component, 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 += GetPackageName(component);
+
+ // Determine the installed size of the package. To do so, we dig
+ // into the Info.plist file from the generated package to retrieve
+ // this size.
+ int installedSize = 0;
+ std::string infoPlistFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ infoPlistFile += ".mpkg/";
+ infoPlistFile += relativePackageLocation;
+ infoPlistFile += "/Contents/Info.plist";
+ bool foundFlagInstalledSize = false;
+ std::string line;
+ std::ifstream ifs(infoPlistFile.c_str());
+ while ( cmSystemTools::GetLineFromStream(ifs, line) )
+ {
+ if (foundFlagInstalledSize)
+ {
+ std::string::size_type pos = line.find("<integer>");
+ if (pos == std::string::npos)
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
+ << infoPlistFile << std::endl
+ << "String is \"" << line << "\"" << std::endl);
+ }
+ else
+ {
+ line.erase(0, pos + 9);
+ pos = line.find("</integer>");
+ if (pos == std::string::npos)
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
+ << infoPlistFile << std::endl);
+ }
+ else
+ {
+ line.erase(pos, std::string::npos);
+ installedSize = atoi(line.c_str());
+ }
+ }
+ foundFlagInstalledSize = false;
+ }
+ else
+ {
+ foundFlagInstalledSize
+ = line.find("IFPkgFlagInstalledSize") != std::string::npos;
+ }
+ }
+
+
+ out << "<pkg-ref id=\"" << packageId << "\" "
+ << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
+ << "installKBytes=\"" << installedSize << "\" "
+ << "auth=\"Admin\" onConclusion=\"None\">"
+ << "file:./" << relativePackageLocation << "</pkg-ref>" << std::endl;
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+AddDependencyAttributes(const cmCPackComponent& component, cmOStringStream& out)
+{
+ std::vector<cmCPackComponent *>::const_iterator dependIt;
+ for (dependIt = component.Dependencies.begin();
+ dependIt != component.Dependencies.end();
+ ++dependIt)
+ {
+ out << " &amp;&amp; choices['" << (*dependIt)->Name << "Choice'].selected";
+ AddDependencyAttributes(**dependIt, out);
+ }
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+AddReverseDependencyAttributes(const cmCPackComponent& component,
+ cmOStringStream& out)
+{
+ std::vector<cmCPackComponent *>::const_iterator dependIt;
+ for (dependIt = component.ReverseDependencies.begin();
+ dependIt != component.ReverseDependencies.end();
+ ++dependIt)
+ {
+ out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
+ AddReverseDependencyAttributes(**dependIt, out);
+ }
+}
+
+//----------------------------------------------------------------------
+std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str)
+{
+ cmSystemTools::ReplaceString(str, "&", "&amp;");
+ cmSystemTools::ReplaceString(str, "<", "&lt;");
+ cmSystemTools::ReplaceString(str, ">", "&gt;");
+ cmSystemTools::ReplaceString(str, "\"", "&quot;");
+ return str;
+}