1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "cmCPackPackageMakerGenerator.h"
15 #include "cmGlobalGenerator.h"
16 #include "cmLocalGenerator.h"
17 #include "cmSystemTools.h"
18 #include "cmMakefile.h"
19 #include "cmGeneratedFileStream.h"
20 #include "cmCPackComponentGroup.h"
21 #include "cmCPackLog.h"
23 #include <cmsys/SystemTools.hxx>
24 #include <cmsys/Glob.hxx>
26 //----------------------------------------------------------------------
27 cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
29 this->PackageMakerVersion = 0.0;
30 this->PackageCompatibilityVersion = 10.4;
33 //----------------------------------------------------------------------
34 cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
38 //----------------------------------------------------------------------
39 bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
41 return this->PackageCompatibilityVersion >= 10.4;
44 //----------------------------------------------------------------------
45 int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
49 std::string dst = resdir;
52 cmSystemTools::CopyFileAlways(script, dst.c_str());
53 cmSystemTools::SetPermissions(dst.c_str(),0777);
54 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
55 "copy script : " << script << "\ninto " << dst.c_str() <<
61 //----------------------------------------------------------------------
62 int cmCPackPackageMakerGenerator::PackageFiles()
65 // It is used! Is this an obsolete comment?
67 std::string resDir; // Where this package's resources will go.
68 std::string packageDirFileName
69 = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
70 if (this->Components.empty())
72 packageDirFileName += ".pkg";
73 resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
74 resDir += "/Resources";
78 packageDirFileName += ".mpkg";
79 if ( !cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str()))
81 cmCPackLogger(cmCPackLog::LOG_ERROR,
82 "unable to create package directory "
83 << packageDirFileName << std::endl);
87 resDir = packageDirFileName;
88 resDir += "/Contents";
89 if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
91 cmCPackLogger(cmCPackLog::LOG_ERROR,
92 "unable to create package subdirectory " << resDir
97 resDir += "/Resources";
98 if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
100 cmCPackLogger(cmCPackLog::LOG_ERROR,
101 "unable to create package subdirectory " << resDir
106 resDir += "/en.lproj";
110 // Create directory structure
111 std::string preflightDirName = resDir + "/PreFlight";
112 std::string postflightDirName = resDir + "/PostFlight";
113 const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
114 const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
115 const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
116 // if preflight or postflight scripts not there create directories
117 // of the same name, I think this makes it work
120 if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str()))
122 cmCPackLogger(cmCPackLog::LOG_ERROR,
123 "Problem creating installer directory: "
124 << preflightDirName.c_str() << std::endl);
130 if ( !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str()))
132 cmCPackLogger(cmCPackLog::LOG_ERROR,
133 "Problem creating installer directory: "
134 << postflightDirName.c_str() << std::endl);
138 // if preflight, postflight, or postupgrade are set
139 // then copy them into the resource directory and make
143 this->CopyInstallScript(resDir.c_str(),
149 this->CopyInstallScript(resDir.c_str(),
155 this->CopyInstallScript(resDir.c_str(),
160 if (!this->Components.empty())
162 // Create the directory where component packages will be built.
163 std::string basePackageDir = packageDirFileName;
164 basePackageDir += "/Contents/Packages";
165 if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
167 cmCPackLogger(cmCPackLog::LOG_ERROR,
168 "Problem creating component packages directory: "
169 << basePackageDir.c_str() << std::endl);
173 // Create the directory where downloaded component packages will
175 const char* userUploadDirectory =
176 this->GetOption("CPACK_UPLOAD_DIRECTORY");
177 std::string uploadDirectory;
178 if (userUploadDirectory && *userUploadDirectory)
180 uploadDirectory = userUploadDirectory;
184 uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
185 uploadDirectory += "/CPackUploads";
188 // Create packages for each component
189 bool warnedAboutDownloadCompatibility = false;
191 std::map<std::string, cmCPackComponent>::iterator compIt;
192 for (compIt = this->Components.begin(); compIt != this->Components.end();
195 std::string packageFile;
196 if (compIt->second.IsDownloaded)
198 if (this->PackageCompatibilityVersion >= 10.5 &&
199 this->PackageMakerVersion >= 3.0)
201 // Build this package within the upload directory.
202 packageFile = uploadDirectory;
204 if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
206 if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
208 cmCPackLogger(cmCPackLog::LOG_ERROR,
209 "Unable to create package upload directory "
210 << uploadDirectory << std::endl);
215 else if (!warnedAboutDownloadCompatibility)
217 if (this->PackageCompatibilityVersion < 10.5)
220 cmCPackLog::LOG_WARNING,
221 "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 "
222 "or greater enable downloaded packages. CPack will build a "
223 "non-downloaded package."
227 if (this->PackageMakerVersion < 3)
229 cmCPackLogger(cmCPackLog::LOG_WARNING,
230 "CPack warning: unable to build downloaded "
231 "packages with PackageMaker versions prior "
232 "to 3.0. CPack will build a non-downloaded package."
236 warnedAboutDownloadCompatibility = true;
240 if (packageFile.empty())
242 // Build this package within the overall distribution
244 packageFile = basePackageDir;
246 // We're not downloading this component, even if the user
248 compIt->second.IsDownloaded = false;
252 packageFile += GetPackageName(compIt->second);
254 std::string packageDir = toplevel;
256 packageDir += compIt->first;
257 if (!this->GenerateComponentPackage(packageFile.c_str(),
265 this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
267 // Copy or create all of the resource files we need.
268 if ( !this->CopyCreateResourceFile("License", resDir.c_str())
269 || !this->CopyCreateResourceFile("ReadMe", resDir.c_str())
270 || !this->CopyCreateResourceFile("Welcome", resDir.c_str())
271 || !this->CopyResourcePlistFile("Info.plist")
272 || !this->CopyResourcePlistFile("Description.plist") )
274 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
279 if (this->Components.empty())
281 // Use PackageMaker to build the package.
282 cmOStringStream pkgCmd;
283 pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
284 << "\" -build -p \"" << packageDirFileName << "\"";
285 if (this->Components.empty())
287 pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
291 pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
294 pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
295 << "/Resources\" -i \""
296 << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
297 << "/Info.plist\" -d \""
298 << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
299 << "/Description.plist\"";
300 if ( this->PackageMakerVersion > 2.0 )
304 if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
309 // We have built the package in place. Generate the
310 // distribution.dist file to describe it for the installer.
311 WriteDistributionFile(packageDirFileName.c_str());
314 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
315 tmpFile += "/hdiutilOutput.log";
316 cmOStringStream dmgCmd;
317 dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
318 << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
319 << "\" \"" << packageFileNames[0] << "\"";
326 res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
327 &retVal, 0, this->GeneratorVerbose,
329 if ( res && !retVal )
334 cmSystemTools::Delay(500);
337 if ( !res || retVal )
339 cmGeneratedFileStream ofs(tmpFile.c_str());
340 ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
341 << "# Output:" << std::endl
342 << output.c_str() << std::endl;
343 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
344 << dmgCmd.str().c_str() << std::endl
345 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
352 //----------------------------------------------------------------------
353 int cmCPackPackageMakerGenerator::InitializeInternal()
355 cmCPackLogger(cmCPackLog::LOG_DEBUG,
356 "cmCPackPackageMakerGenerator::Initialize()" << std::endl);
357 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
359 // Starting with Xcode 4.3, PackageMaker is a separate app, and you
360 // can put it anywhere you want. So... use a variable for its location.
361 // People who put it in unexpected places can use the variable to tell
364 // Use the following locations, in "most recent installation" order,
365 // to search for the PackageMaker app. Assume people who copy it into
366 // the new Xcode 4.3 app in "/Applications" will copy it into the nested
367 // Applications folder inside the Xcode bundle itself. Or directly in
368 // the "/Applications" directory.
370 // If found, save result in the CPACK_INSTALLER_PROGRAM variable.
372 std::vector<std::string> paths;
374 "/Applications/Xcode.app/Contents/Applications"
375 "/PackageMaker.app/Contents/MacOS");
377 "/Applications/Utilities"
378 "/PackageMaker.app/Contents/MacOS");
381 "/PackageMaker.app/Contents/MacOS");
383 "/Developer/Applications/Utilities"
384 "/PackageMaker.app/Contents/MacOS");
386 "/Developer/Applications"
387 "/PackageMaker.app/Contents/MacOS");
390 const char *inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
391 if (inst_program && *inst_program)
393 pkgPath = inst_program;
397 pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false);
398 if ( pkgPath.empty() )
400 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler"
404 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
407 // Get path to the real PackageMaker, not a symlink:
408 pkgPath = cmSystemTools::GetRealPath(pkgPath.c_str());
409 // Up from there to find the version.plist file in the "Contents" dir:
410 std::string contents_dir;
411 contents_dir = cmSystemTools::GetFilenamePath(pkgPath);
412 contents_dir = cmSystemTools::GetFilenamePath(contents_dir);
414 std::string versionFile = contents_dir + "/version.plist";
416 if ( !cmSystemTools::FileExists(versionFile.c_str()) )
418 cmCPackLogger(cmCPackLog::LOG_ERROR,
419 "Cannot find PackageMaker compiler version file: "
420 << versionFile.c_str()
425 std::ifstream ifs(versionFile.c_str());
428 cmCPackLogger(cmCPackLog::LOG_ERROR,
429 "Cannot open PackageMaker compiler version file" << std::endl);
433 // Check the PackageMaker version
434 cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
435 cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
437 bool foundKey = false;
438 while ( cmSystemTools::GetLineFromStream(ifs, line) )
440 if ( rexKey.find(line) )
448 cmCPackLogger(cmCPackLog::LOG_ERROR,
449 "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
450 "version file" << std::endl);
453 if ( !cmSystemTools::GetLineFromStream(ifs, line) ||
454 !rexVersion.find(line) )
456 cmCPackLogger(cmCPackLog::LOG_ERROR,
457 "Problem reading the PackageMaker compiler version file: "
458 << versionFile.c_str() << std::endl);
461 this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
462 if ( this->PackageMakerVersion < 1.0 )
464 cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher"
468 cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: "
469 << this->PackageMakerVersion << std::endl);
471 // Determine the package compatibility version. If it wasn't
472 // specified by the user, we define it based on which features the
474 const char *packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
475 if (packageCompat && *packageCompat)
477 this->PackageCompatibilityVersion = atof(packageCompat);
479 else if (this->GetOption("CPACK_DOWNLOAD_SITE"))
481 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5");
482 this->PackageCompatibilityVersion = 10.5;
484 else if (this->GetOption("CPACK_COMPONENTS_ALL"))
486 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4");
487 this->PackageCompatibilityVersion = 10.4;
491 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
492 this->PackageCompatibilityVersion = 10.3;
495 std::vector<std::string> no_paths;
496 pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false);
497 if ( pkgPath.empty() )
499 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
503 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
506 return this->Superclass::InitializeInternal();
509 //----------------------------------------------------------------------
510 bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name,
513 std::string uname = cmSystemTools::UpperCase(name);
514 std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
515 const char* inFileName = this->GetOption(cpackVar.c_str());
518 cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str()
519 << " not specified. It should point to "
520 << (name ? name : "(NULL)")
522 << ".html, or " << name << ".txt file" << std::endl);
525 if ( !cmSystemTools::FileExists(inFileName) )
527 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
528 << (name ? name : "(NULL)")
529 << " resource file: " << inFileName << std::endl);
532 std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
533 if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" )
535 cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: "
536 << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed."
541 std::string destFileName = dirName;
543 destFileName += name + ext;
545 // Set this so that distribution.dist gets the right name (without
547 this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(),
548 (name + ext).c_str());
550 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
551 << (inFileName ? inFileName : "(NULL)")
552 << " to " << destFileName.c_str() << std::endl);
553 this->ConfigureFile(inFileName, destFileName.c_str());
557 bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
565 std::string inFName = "CPack.";
568 std::string inFileName = this->FindTemplate(inFName.c_str());
569 if ( inFileName.empty() )
571 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
572 << inFName << std::endl);
576 std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
578 destFileName += outName;
580 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
581 << inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
582 this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
586 //----------------------------------------------------------------------
587 bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command,
588 const char *packageFile)
590 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
591 tmpFile += "/PackageMakerOutput.log";
593 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
596 bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0,
597 this->GeneratorVerbose, 0);
598 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
600 if ( !res || retVal )
602 cmGeneratedFileStream ofs(tmpFile.c_str());
603 ofs << "# Run command: " << command << std::endl
604 << "# Output:" << std::endl
605 << output.c_str() << std::endl;
606 cmCPackLogger(cmCPackLog::LOG_ERROR,
607 "Problem running PackageMaker command: " << command
608 << std::endl << "Please check " << tmpFile.c_str() << " for errors"
612 // sometimes the command finishes but the directory is not yet
613 // created, so try 10 times to see if it shows up
616 !cmSystemTools::FileExists(packageFile))
618 cmSystemTools::Delay(500);
621 if(!cmSystemTools::FileExists(packageFile))
624 cmCPackLog::LOG_ERROR,
625 "Problem running PackageMaker command: " << command
626 << std::endl << "Package not created: " << packageFile
634 //----------------------------------------------------------------------
636 cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component)
638 if (component.ArchiveFile.empty())
640 std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
641 packagesDir += ".dummy";
643 out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
644 << "-" << component.Name << ".pkg";
649 return component.ArchiveFile + ".pkg";
653 //----------------------------------------------------------------------
655 cmCPackPackageMakerGenerator::
656 GenerateComponentPackage(const char *packageFile,
657 const char *packageDir,
658 const cmCPackComponent& component)
660 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
661 "- Building component package: " <<
662 packageFile << std::endl);
664 // The command that will be used to run PackageMaker
665 cmOStringStream pkgCmd;
667 if (this->PackageCompatibilityVersion < 10.5 ||
668 this->PackageMakerVersion < 3.0)
670 // Create Description.plist and Info.plist files for normal Mac OS
671 // X packages, which work on Mac OS X 10.3 and newer.
672 std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
673 descriptionFile += '/' + component.Name + "-Description.plist";
674 std::ofstream out(descriptionFile.c_str());
675 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
676 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
677 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl
678 << "<plist version=\"1.4\">" << std::endl
679 << "<dict>" << std::endl
680 << " <key>IFPkgDescriptionTitle</key>" << std::endl
681 << " <string>" << component.DisplayName << "</string>" << std::endl
682 << " <key>IFPkgDescriptionVersion</key>" << std::endl
683 << " <string>" << this->GetOption("CPACK_PACKAGE_VERSION")
684 << "</string>" << std::endl
685 << " <key>IFPkgDescriptionDescription</key>" << std::endl
686 << " <string>" + this->EscapeForXML(component.Description)
687 << "</string>" << std::endl
688 << "</dict>" << std::endl
689 << "</plist>" << std::endl;
692 // Create the Info.plist file for this component
693 std::string moduleVersionSuffix = ".";
694 moduleVersionSuffix += component.Name;
695 this->SetOption("CPACK_MODULE_VERSION_SUFFIX",
696 moduleVersionSuffix.c_str());
697 std::string infoFileName = component.Name;
698 infoFileName += "-Info.plist";
699 if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str()))
704 pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
705 << "\" -build -p \"" << packageFile << "\""
706 << " -f \"" << packageDir << "\""
707 << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
708 << "/" << infoFileName << "\""
709 << " -d \"" << descriptionFile << "\"";
713 // Create a "flat" package on Mac OS X 10.5 and newer. Flat
714 // packages are stored in a single file, rather than a directory
715 // like normal packages, and can be downloaded by the installer
716 // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create
717 // flat packages when the packages will be downloaded on the fly.
718 std::string pkgId = "com.";
719 pkgId += this->GetOption("CPACK_PACKAGE_VENDOR");
721 pkgId += this->GetOption("CPACK_PACKAGE_NAME");
723 pkgId += component.Name;
725 pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
726 << "\" --root \"" << packageDir << "\""
728 << " --target " << this->GetOption("CPACK_OSX_PACKAGE_VERSION")
729 << " --out \"" << packageFile << "\"";
733 return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
736 //----------------------------------------------------------------------
738 cmCPackPackageMakerGenerator::
739 WriteDistributionFile(const char* metapackageFile)
741 std::string distributionTemplate
742 = this->FindTemplate("CPack.distribution.dist.in");
743 if ( distributionTemplate.empty() )
745 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
746 << distributionTemplate << std::endl);
750 std::string distributionFile = metapackageFile;
751 distributionFile += "/Contents/distribution.dist";
753 // Create the choice outline, which provides a tree-based view of
754 // the components in their groups.
755 cmOStringStream choiceOut;
756 choiceOut << "<choices-outline>" << std::endl;
758 // Emit the outline for the groups
759 std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
760 for (groupIt = this->ComponentGroups.begin();
761 groupIt != this->ComponentGroups.end();
764 if (groupIt->second.ParentGroup == 0)
766 CreateChoiceOutline(groupIt->second, choiceOut);
770 // Emit the outline for the non-grouped components
771 std::map<std::string, cmCPackComponent>::iterator compIt;
772 for (compIt = this->Components.begin(); compIt != this->Components.end();
775 if (!compIt->second.Group)
777 choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>"
781 choiceOut << "</choices-outline>" << std::endl;
783 // Create the actual choices
784 for (groupIt = this->ComponentGroups.begin();
785 groupIt != this->ComponentGroups.end();
788 CreateChoice(groupIt->second, choiceOut);
790 for (compIt = this->Components.begin(); compIt != this->Components.end();
793 CreateChoice(compIt->second, choiceOut);
795 this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
797 // Create the distribution.dist file in the metapackage to turn it
798 // into a distribution package.
799 this->ConfigureFile(distributionTemplate.c_str(),
800 distributionFile.c_str());
803 //----------------------------------------------------------------------
805 cmCPackPackageMakerGenerator::
806 CreateChoiceOutline(const cmCPackComponentGroup& group, cmOStringStream& out)
808 out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
809 std::vector<cmCPackComponentGroup*>::const_iterator groupIt;
810 for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end();
813 CreateChoiceOutline(**groupIt, out);
816 std::vector<cmCPackComponent*>::const_iterator compIt;
817 for (compIt = group.Components.begin(); compIt != group.Components.end();
820 out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>"
823 out << "</line>" << std::endl;
826 //----------------------------------------------------------------------
828 cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponentGroup& group,
829 cmOStringStream& out)
831 out << "<choice id=\"" << group.Name << "Choice\" "
832 << "title=\"" << group.DisplayName << "\" "
833 << "start_selected=\"true\" "
834 << "start_enabled=\"true\" "
835 << "start_visible=\"true\" ";
836 if (!group.Description.empty())
838 out << "description=\"" << EscapeForXML(group.Description)
841 out << "></choice>" << std::endl;
844 //----------------------------------------------------------------------
846 cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponent& component,
847 cmOStringStream& out)
849 std::string packageId = "com.";
850 packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
852 packageId += this->GetOption("CPACK_PACKAGE_NAME");
854 packageId += component.Name;
856 out << "<choice id=\"" << component.Name << "Choice\" "
857 << "title=\"" << component.DisplayName << "\" "
858 << "start_selected=\""
859 << (component.IsDisabledByDefault &&
860 !component.IsRequired? "false" : "true")
862 << "start_enabled=\""
863 << (component.IsRequired? "false" : "true")
865 << "start_visible=\"" << (component.IsHidden? "false" : "true") << "\" ";
866 if (!component.Description.empty())
868 out << "description=\"" << EscapeForXML(component.Description)
871 if (!component.Dependencies.empty() ||
872 !component.ReverseDependencies.empty())
874 // The "selected" expression is evaluated each time any choice is
875 // selected, for all choices *except* the one that the user
876 // selected. A component is marked selected if it has been
877 // selected (my.choice.selected in Javascript) and all of the
878 // components it depends on have been selected (transitively) or
879 // if any of the components that depend on it have been selected
880 // (transitively). Assume that we have components A, B, C, D, and
881 // E, where each component depends on the previous component (B
882 // depends on A, C depends on B, D depends on C, and E depends on
883 // D). The expression we build for the component C will be
884 // my.choice.selected && B && A || D || E
885 // This way, selecting C will automatically select everything it depends
886 // on (B and A), while selecting something that depends on C--either D
887 // or E--will automatically cause C to get selected.
888 out << "selected=\"my.choice.selected";
889 std::set<const cmCPackComponent *> visited;
890 AddDependencyAttributes(component, visited, out);
892 AddReverseDependencyAttributes(component, visited, out);
895 out << ">" << std::endl;
896 out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl;
897 out << "</choice>" << std::endl;
899 // Create a description of the package associated with this
901 std::string relativePackageLocation = "Contents/Packages/";
902 relativePackageLocation += this->GetPackageName(component);
904 // Determine the installed size of the package.
905 std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
907 dirName += component.Name;
908 unsigned long installedSize
909 = component.GetInstalledSizeInKbytes(dirName.c_str());
911 out << "<pkg-ref id=\"" << packageId << "\" "
912 << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
913 << "installKBytes=\"" << installedSize << "\" "
914 << "auth=\"Admin\" onConclusion=\"None\">";
915 if (component.IsDownloaded)
917 out << this->GetOption("CPACK_DOWNLOAD_SITE")
918 << this->GetPackageName(component);
922 out << "file:./" << relativePackageLocation;
924 out << "</pkg-ref>" << std::endl;
927 //----------------------------------------------------------------------
929 cmCPackPackageMakerGenerator::
930 AddDependencyAttributes(const cmCPackComponent& component,
931 std::set<const cmCPackComponent *>& visited,
932 cmOStringStream& out)
934 if (visited.find(&component) != visited.end())
938 visited.insert(&component);
940 std::vector<cmCPackComponent *>::const_iterator dependIt;
941 for (dependIt = component.Dependencies.begin();
942 dependIt != component.Dependencies.end();
945 out << " && choices['" <<
946 (*dependIt)->Name << "Choice'].selected";
947 AddDependencyAttributes(**dependIt, visited, out);
951 //----------------------------------------------------------------------
953 cmCPackPackageMakerGenerator::
954 AddReverseDependencyAttributes(const cmCPackComponent& component,
955 std::set<const cmCPackComponent *>& visited,
956 cmOStringStream& out)
958 if (visited.find(&component) != visited.end())
962 visited.insert(&component);
964 std::vector<cmCPackComponent *>::const_iterator dependIt;
965 for (dependIt = component.ReverseDependencies.begin();
966 dependIt != component.ReverseDependencies.end();
969 out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
970 AddReverseDependencyAttributes(**dependIt, visited, out);
974 //----------------------------------------------------------------------
975 std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str)
977 cmSystemTools::ReplaceString(str, "&", "&");
978 cmSystemTools::ReplaceString(str, "<", "<");
979 cmSystemTools::ReplaceString(str, ">", ">");
980 cmSystemTools::ReplaceString(str, "\"", """);