summaryrefslogtreecommitdiff
path: root/Source/CPack/cmCPackPackageMakerGenerator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CPack/cmCPackPackageMakerGenerator.cxx')
-rw-r--r--Source/CPack/cmCPackPackageMakerGenerator.cxx982
1 files changed, 982 insertions, 0 deletions
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx
new file mode 100644
index 000000000..3a0e89bbd
--- /dev/null
+++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx
@@ -0,0 +1,982 @@
+/*============================================================================
+ 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 "cmCPackPackageMakerGenerator.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>
+
+//----------------------------------------------------------------------
+cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
+{
+ this->PackageMakerVersion = 0.0;
+ this->PackageCompatibilityVersion = 10.4;
+}
+
+//----------------------------------------------------------------------
+cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
+{
+}
+
+//----------------------------------------------------------------------
+bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
+{
+ return this->PackageCompatibilityVersion >= 10.4;
+}
+
+//----------------------------------------------------------------------
+int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
+ const char* script,
+ const char* name)
+{
+ std::string dst = resdir;
+ dst += "/";
+ dst += name;
+ cmSystemTools::CopyFileAlways(script, 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
+ // It is used! Is this an obsolete comment?
+
+ std::string resDir; // Where this package's resources will go.
+ std::string packageDirFileName
+ = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ if (this->Components.empty())
+ {
+ packageDirFileName += ".pkg";
+ resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ resDir += "/Resources";
+ }
+ else
+ {
+ packageDirFileName += ".mpkg";
+ if ( !cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "unable to create package directory "
+ << packageDirFileName << std::endl);
+ return 0;
+ }
+
+ resDir = packageDirFileName;
+ resDir += "/Contents";
+ if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "unable to create package subdirectory " << resDir
+ << std::endl);
+ return 0;
+ }
+
+ resDir += "/Resources";
+ if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "unable to create package subdirectory " << resDir
+ << std::endl);
+ return 0;
+ }
+
+ resDir += "/en.lproj";
+ }
+
+
+ // Create directory structure
+ std::string preflightDirName = resDir + "/PreFlight";
+ std::string postflightDirName = resDir + "/PostFlight";
+ const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
+ const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
+ const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
+ // if preflight or postflight scripts not there create directories
+ // of the same name, I think this makes it work
+ if(!preflight)
+ {
+ if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem creating installer directory: "
+ << preflightDirName.c_str() << std::endl);
+ return 0;
+ }
+ }
+ if(!postflight)
+ {
+ if ( !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem creating installer directory: "
+ << postflightDirName.c_str() << std::endl);
+ return 0;
+ }
+ }
+ // if preflight, postflight, or postupgrade are set
+ // then copy them into the resource directory and make
+ // them executable
+ if(preflight)
+ {
+ this->CopyInstallScript(resDir.c_str(),
+ preflight,
+ "preflight");
+ }
+ if(postflight)
+ {
+ this->CopyInstallScript(resDir.c_str(),
+ postflight,
+ "postflight");
+ }
+ if(postupgrade)
+ {
+ this->CopyInstallScript(resDir.c_str(),
+ postupgrade,
+ "postupgrade");
+ }
+
+ if (!this->Components.empty())
+ {
+ // 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;
+ }
+
+ // Create the directory where downloaded component packages will
+ // be placed.
+ const char* userUploadDirectory =
+ this->GetOption("CPACK_UPLOAD_DIRECTORY");
+ std::string uploadDirectory;
+ if (userUploadDirectory && *userUploadDirectory)
+ {
+ uploadDirectory = userUploadDirectory;
+ }
+ else
+ {
+ uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
+ uploadDirectory += "/CPackUploads";
+ }
+
+ // Create packages for each component
+ bool warnedAboutDownloadCompatibility = false;
+
+ std::map<std::string, cmCPackComponent>::iterator compIt;
+ for (compIt = this->Components.begin(); compIt != this->Components.end();
+ ++compIt)
+ {
+ std::string packageFile;
+ if (compIt->second.IsDownloaded)
+ {
+ if (this->PackageCompatibilityVersion >= 10.5 &&
+ this->PackageMakerVersion >= 3.0)
+ {
+ // Build this package within the upload directory.
+ packageFile = uploadDirectory;
+
+ if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
+ {
+ if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to create package upload directory "
+ << uploadDirectory << std::endl);
+ return 0;
+ }
+ }
+ }
+ else if (!warnedAboutDownloadCompatibility)
+ {
+ if (this->PackageCompatibilityVersion < 10.5)
+ {
+ cmCPackLogger(
+ cmCPackLog::LOG_WARNING,
+ "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 "
+ "or greater enable downloaded packages. CPack will build a "
+ "non-downloaded package."
+ << std::endl);
+ }
+
+ if (this->PackageMakerVersion < 3)
+ {
+ cmCPackLogger(cmCPackLog::LOG_WARNING,
+ "CPack warning: unable to build downloaded "
+ "packages with PackageMaker versions prior "
+ "to 3.0. CPack will build a non-downloaded package."
+ << std::endl);
+ }
+
+ warnedAboutDownloadCompatibility = true;
+ }
+ }
+
+ if (packageFile.empty())
+ {
+ // Build this package within the overall distribution
+ // metapackage.
+ packageFile = basePackageDir;
+
+ // We're not downloading this component, even if the user
+ // requested it.
+ compIt->second.IsDownloaded = false;
+ }
+
+ 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", resDir.c_str())
+ || !this->CopyCreateResourceFile("ReadMe", resDir.c_str())
+ || !this->CopyCreateResourceFile("Welcome", resDir.c_str())
+ || !this->CopyResourcePlistFile("Info.plist")
+ || !this->CopyResourcePlistFile("Description.plist") )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
+ << std::endl);
+ return 0;
+ }
+
+ if (this->Components.empty())
+ {
+ // Use PackageMaker to build the package.
+ cmOStringStream pkgCmd;
+ pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
+ << "\" -build -p \"" << packageDirFileName << "\"";
+ if (this->Components.empty())
+ {
+ pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ }
+ else
+ {
+ pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
+ << "/packages/";
+ }
+ 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 )
+ {
+ pkgCmd << " -v";
+ }
+ if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
+ return 0;
+ }
+ else
+ {
+ // We have built the package in place. Generate the
+ // distribution.dist file to describe it for the installer.
+ WriteDistributionFile(packageDirFileName.c_str());
+ }
+
+ 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
+ << "\" \"" << packageFileNames[0] << "\"";
+ std::string output;
+ int retVal = 1;
+ int numTries = 10;
+ bool res = false;
+ while(numTries > 0)
+ {
+ res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
+ &retVal, 0, this->GeneratorVerbose,
+ 0);
+ if ( res && !retVal )
+ {
+ numTries = -1;
+ break;
+ }
+ cmSystemTools::Delay(500);
+ numTries--;
+ }
+ if ( !res || retVal )
+ {
+ cmGeneratedFileStream ofs(tmpFile.c_str());
+ ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
+ << "# Output:" << std::endl
+ << output.c_str() << std::endl;
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
+ << dmgCmd.str().c_str() << std::endl
+ << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
+ return 0;
+ }
+
+ return 1;
+}
+
+//----------------------------------------------------------------------
+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
+ // can put it anywhere you want. So... use a variable for its location.
+ // People who put it in unexpected places can use the variable to tell
+ // us where it is.
+ //
+ // Use the following locations, in "most recent installation" order,
+ // to search for the PackageMaker app. Assume people who copy it into
+ // the new Xcode 4.3 app in "/Applications" will copy it into the nested
+ // Applications folder inside the Xcode bundle itself. Or directly in
+ // the "/Applications" directory.
+ //
+ // If found, save result in the CPACK_INSTALLER_PROGRAM variable.
+
+ std::vector<std::string> paths;
+ paths.push_back(
+ "/Applications/Xcode.app/Contents/Applications"
+ "/PackageMaker.app/Contents/MacOS");
+ paths.push_back(
+ "/Applications/Utilities"
+ "/PackageMaker.app/Contents/MacOS");
+ paths.push_back(
+ "/Applications"
+ "/PackageMaker.app/Contents/MacOS");
+ paths.push_back(
+ "/Developer/Applications/Utilities"
+ "/PackageMaker.app/Contents/MacOS");
+ paths.push_back(
+ "/Developer/Applications"
+ "/PackageMaker.app/Contents/MacOS");
+
+ std::string pkgPath;
+ const char *inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
+ if (inst_program && *inst_program)
+ {
+ pkgPath = inst_program;
+ }
+ else
+ {
+ pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false);
+ if ( pkgPath.empty() )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler"
+ << std::endl);
+ return 0;
+ }
+ this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
+ }
+
+ // Get path to the real PackageMaker, not a symlink:
+ pkgPath = cmSystemTools::GetRealPath(pkgPath.c_str());
+ // Up from there to find the version.plist file in the "Contents" dir:
+ std::string contents_dir;
+ contents_dir = cmSystemTools::GetFilenamePath(pkgPath);
+ contents_dir = cmSystemTools::GetFilenamePath(contents_dir);
+
+ std::string versionFile = contents_dir + "/version.plist";
+
+ if ( !cmSystemTools::FileExists(versionFile.c_str()) )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Cannot find PackageMaker compiler version file: "
+ << versionFile.c_str()
+ << std::endl);
+ return 0;
+ }
+
+ std::ifstream ifs(versionFile.c_str());
+ if ( !ifs )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Cannot open PackageMaker compiler version file" << std::endl);
+ return 0;
+ }
+
+ // Check the PackageMaker version
+ cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
+ cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
+ std::string line;
+ bool foundKey = false;
+ while ( cmSystemTools::GetLineFromStream(ifs, line) )
+ {
+ if ( rexKey.find(line) )
+ {
+ foundKey = true;
+ break;
+ }
+ }
+ if ( !foundKey )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
+ "version file" << std::endl);
+ return 0;
+ }
+ if ( !cmSystemTools::GetLineFromStream(ifs, line) ||
+ !rexVersion.find(line) )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Problem reading the PackageMaker compiler version file: "
+ << versionFile.c_str() << std::endl);
+ return 0;
+ }
+ this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
+ if ( this->PackageMakerVersion < 1.0 )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher"
+ << std::endl);
+ return 0;
+ }
+ cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: "
+ << this->PackageMakerVersion << std::endl);
+
+ // Determine the package compatibility version. If it wasn't
+ // specified by the user, we define it based on which features the
+ // user requested.
+ const char *packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
+ if (packageCompat && *packageCompat)
+ {
+ this->PackageCompatibilityVersion = atof(packageCompat);
+ }
+ else if (this->GetOption("CPACK_DOWNLOAD_SITE"))
+ {
+ this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5");
+ this->PackageCompatibilityVersion = 10.5;
+ }
+ else if (this->GetOption("CPACK_COMPONENTS_ALL"))
+ {
+ this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4");
+ this->PackageCompatibilityVersion = 10.4;
+ }
+ else
+ {
+ this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
+ this->PackageCompatibilityVersion = 10.3;
+ }
+
+ std::vector<std::string> no_paths;
+ pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false);
+ if ( pkgPath.empty() )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
+ << std::endl);
+ return 0;
+ }
+ this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
+ pkgPath.c_str());
+
+ return this->Superclass::InitializeInternal();
+}
+
+//----------------------------------------------------------------------
+bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name,
+ const char* 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 ? name : "(NULL)")
+ << ".rtf, " << name
+ << ".html, or " << name << ".txt file" << std::endl);
+ return false;
+ }
+ if ( !cmSystemTools::FileExists(inFileName) )
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
+ << (name ? name : "(NULL)")
+ << " 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 char* name,
+ const char* outName)
+{
+ if (!outName)
+ {
+ outName = name;
+ }
+
+ 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)
+{
+ 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)
+{
+ if (component.ArchiveFile.empty())
+ {
+ std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ packagesDir += ".dummy";
+ cmOStringStream 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)
+{
+ cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+ "- Building component package: " <<
+ packageFile << std::endl);
+
+ // The command that will be used to run PackageMaker
+ cmOStringStream pkgCmd;
+
+ if (this->PackageCompatibilityVersion < 10.5 ||
+ this->PackageMakerVersion < 3.0)
+ {
+ // Create Description.plist and Info.plist files for normal Mac OS
+ // X packages, which work on Mac OS X 10.3 and newer.
+ 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;
+ }
+
+ pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
+ << "\" -build -p \"" << packageFile << "\""
+ << " -f \"" << packageDir << "\""
+ << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
+ << "/" << infoFileName << "\""
+ << " -d \"" << descriptionFile << "\"";
+ }
+ else
+ {
+ // Create a "flat" package on Mac OS X 10.5 and newer. Flat
+ // packages are stored in a single file, rather than a directory
+ // like normal packages, and can be downloaded by the installer
+ // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create
+ // flat packages when the packages will be downloaded on the fly.
+ std::string pkgId = "com.";
+ pkgId += this->GetOption("CPACK_PACKAGE_VENDOR");
+ pkgId += '.';
+ pkgId += this->GetOption("CPACK_PACKAGE_NAME");
+ pkgId += '.';
+ pkgId += component.Name;
+
+ pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
+ << "\" --root \"" << packageDir << "\""
+ << " --id " << pkgId
+ << " --target " << this->GetOption("CPACK_OSX_PACKAGE_VERSION")
+ << " --out \"" << packageFile << "\"";
+ }
+
+ // 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.
+ 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)
+ {
+ 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;
+ }
+ }
+ 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<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,
+ 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 += 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;
+ 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,
+ cmOStringStream& 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 << " &amp;&amp; choices['" <<
+ (*dependIt)->Name << "Choice'].selected";
+ AddDependencyAttributes(**dependIt, visited, out);
+ }
+}
+
+//----------------------------------------------------------------------
+void
+cmCPackPackageMakerGenerator::
+AddReverseDependencyAttributes(const cmCPackComponent& component,
+ std::set<const cmCPackComponent *>& visited,
+ cmOStringStream& 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, "&", "&amp;");
+ cmSystemTools::ReplaceString(str, "<", "&lt;");
+ cmSystemTools::ReplaceString(str, ">", "&gt;");
+ cmSystemTools::ReplaceString(str, "\"", "&quot;");
+ return str;
+}