Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / CPack / cmCPackPackageMakerGenerator.cxx
blob63e75717e2136b6a2d28a8e4720bbe157b3c7fef
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackPackageMakerGenerator.cxx,v $
5 Language: C++
6 Date: $Date: 2008-10-01 13:04:27 $
7 Version: $Revision: 1.29 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "cmCPackPackageMakerGenerator.h"
19 #include "cmake.h"
20 #include "cmGlobalGenerator.h"
21 #include "cmLocalGenerator.h"
22 #include "cmSystemTools.h"
23 #include "cmMakefile.h"
24 #include "cmGeneratedFileStream.h"
25 #include "cmCPackComponentGroup.h"
26 #include "cmCPackLog.h"
28 #include <cmsys/SystemTools.hxx>
29 #include <cmsys/Glob.hxx>
31 //----------------------------------------------------------------------
32 cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
34 this->PackageMakerVersion = 0.0;
35 this->PackageCompatibilityVersion = 10.4;
38 //----------------------------------------------------------------------
39 cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
43 //----------------------------------------------------------------------
44 bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
46 return this->PackageCompatibilityVersion >= 10.4;
49 //----------------------------------------------------------------------
50 int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
51 const char* script,
52 const char* name)
54 std::string dst = resdir;
55 dst += "/";
56 dst += name;
57 cmSystemTools::CopyFileAlways(script, dst.c_str());
58 cmSystemTools::SetPermissions(dst.c_str(),0777);
59 cmCPackLogger(cmCPackLog::LOG_VERBOSE,
60 "copy script : " << script << "\ninto " << dst.c_str() <<
61 std::endl);
63 return 1;
66 //----------------------------------------------------------------------
67 int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
68 const char* toplevel,
69 const std::vector<std::string>& files)
71 (void) files; // TODO: Fix api to not need files.
72 (void) toplevel; // TODO: Use toplevel
74 std::string resDir; // Where this package's resources will go.
75 std::string packageDirFileName
76 = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
77 if (this->Components.empty())
79 packageDirFileName += ".pkg";
80 resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
81 resDir += "/Resources";
83 else
85 packageDirFileName += ".mpkg";
86 if ( !cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str()))
88 cmCPackLogger(cmCPackLog::LOG_ERROR,
89 "unable to create package directory "
90 << packageDirFileName << std::endl);
91 return 0;
94 resDir = packageDirFileName;
95 resDir += "/Contents";
96 if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
98 cmCPackLogger(cmCPackLog::LOG_ERROR,
99 "unable to create package subdirectory " << resDir
100 << std::endl);
101 return 0;
104 resDir += "/Resources";
105 if ( !cmsys::SystemTools::MakeDirectory(resDir.c_str()))
107 cmCPackLogger(cmCPackLog::LOG_ERROR,
108 "unable to create package subdirectory " << resDir
109 << std::endl);
110 return 0;
113 resDir += "/en.lproj";
117 // Create directory structure
118 std::string preflightDirName = resDir + "/PreFlight";
119 std::string postflightDirName = resDir + "/PostFlight";
120 const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
121 const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
122 const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
123 // if preflight or postflight scripts not there create directories
124 // of the same name, I think this makes it work
125 if(!preflight)
127 if ( !cmsys::SystemTools::MakeDirectory(preflightDirName.c_str()))
129 cmCPackLogger(cmCPackLog::LOG_ERROR,
130 "Problem creating installer directory: "
131 << preflightDirName.c_str() << std::endl);
132 return 0;
135 if(!postflight)
137 if ( !cmsys::SystemTools::MakeDirectory(postflightDirName.c_str()))
139 cmCPackLogger(cmCPackLog::LOG_ERROR,
140 "Problem creating installer directory: "
141 << postflightDirName.c_str() << std::endl);
142 return 0;
145 // if preflight, postflight, or postupgrade are set
146 // then copy them into the resource directory and make
147 // them executable
148 if(preflight)
150 this->CopyInstallScript(resDir.c_str(),
151 preflight,
152 "preflight");
154 if(postflight)
156 this->CopyInstallScript(resDir.c_str(),
157 postflight,
158 "postflight");
160 if(postupgrade)
162 this->CopyInstallScript(resDir.c_str(),
163 postupgrade,
164 "postupgrade");
167 if (!this->Components.empty())
169 // Create the directory where component packages will be built.
170 std::string basePackageDir = packageDirFileName;
171 basePackageDir += "/Contents/Packages";
172 if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
174 cmCPackLogger(cmCPackLog::LOG_ERROR,
175 "Problem creating component packages directory: "
176 << basePackageDir.c_str() << std::endl);
177 return 0;
180 // Create the directory where downloaded component packages will
181 // be placed.
182 const char* userUploadDirectory =
183 this->GetOption("CPACK_UPLOAD_DIRECTORY");
184 std::string uploadDirectory;
185 if (userUploadDirectory && *userUploadDirectory)
187 uploadDirectory = userUploadDirectory;
189 else
191 uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
192 uploadDirectory += "/CPackUploads";
195 // Create packages for each component
196 bool warnedAboutDownloadCompatibility = false;
198 std::map<std::string, cmCPackComponent>::iterator compIt;
199 for (compIt = this->Components.begin(); compIt != this->Components.end();
200 ++compIt)
202 std::string packageFile;
203 if (compIt->second.IsDownloaded)
205 if (this->PackageCompatibilityVersion >= 10.5 &&
206 this->PackageMakerVersion >= 3.0)
208 // Build this package within the upload directory.
209 packageFile = uploadDirectory;
211 if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
213 if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
215 cmCPackLogger(cmCPackLog::LOG_ERROR,
216 "Unable to create package upload directory "
217 << uploadDirectory << std::endl);
218 return 0;
222 else if (!warnedAboutDownloadCompatibility)
224 if (this->PackageCompatibilityVersion < 10.5)
226 cmCPackLogger(
227 cmCPackLog::LOG_WARNING,
228 "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 "
229 "or greater enable downloaded packages. CPack will build a "
230 "non-downloaded package."
231 << std::endl);
234 if (this->PackageMakerVersion < 3)
236 cmCPackLogger(cmCPackLog::LOG_WARNING,
237 "CPack warning: unable to build downloaded "
238 "packages with PackageMaker versions prior "
239 "to 3.0. CPack will build a non-downloaded package."
240 << std::endl);
243 warnedAboutDownloadCompatibility = true;
247 if (packageFile.empty())
249 // Build this package within the overall distribution
250 // metapackage.
251 packageFile = basePackageDir;
253 // We're not downloading this component, even if the user
254 // requested it.
255 compIt->second.IsDownloaded = false;
258 packageFile += '/';
259 packageFile += GetPackageName(compIt->second);
261 std::string packageDir = toplevel;
262 packageDir += '/';
263 packageDir += compIt->first;
264 if (!this->GenerateComponentPackage(packageFile.c_str(),
265 packageDir.c_str(),
266 compIt->second))
268 return 0;
272 this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
274 // Copy or create all of the resource files we need.
275 if ( !this->CopyCreateResourceFile("License", resDir.c_str())
276 || !this->CopyCreateResourceFile("ReadMe", resDir.c_str())
277 || !this->CopyCreateResourceFile("Welcome", resDir.c_str())
278 || !this->CopyResourcePlistFile("Info.plist")
279 || !this->CopyResourcePlistFile("Description.plist") )
281 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
282 << std::endl);
283 return 0;
286 if (this->Components.empty())
288 // Use PackageMaker to build the package.
289 cmOStringStream pkgCmd;
290 pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
291 << "\" -build -p \"" << packageDirFileName << "\"";
292 if (this->Components.empty())
294 pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
296 else
298 pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
299 << "/packages/";
301 pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
302 << "/Resources\" -i \""
303 << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
304 << "/Info.plist\" -d \""
305 << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
306 << "/Description.plist\"";
307 if ( this->PackageMakerVersion > 2.0 )
309 pkgCmd << " -v";
311 if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
312 return 0;
314 else
316 // We have built the package in place. Generate the
317 // distribution.dist file to describe it for the installer.
318 WriteDistributionFile(packageDirFileName.c_str());
321 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
322 tmpFile += "/hdiutilOutput.log";
323 cmOStringStream dmgCmd;
324 dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
325 << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
326 << "\" \"" << outFileName << "\"";
327 std::string output;
328 int retVal = 1;
329 bool res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
330 &retVal, 0, this->GeneratorVerbose, 0);
331 if ( !res || retVal )
333 cmGeneratedFileStream ofs(tmpFile.c_str());
334 ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
335 << "# Output:" << std::endl
336 << output.c_str() << std::endl;
337 cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
338 << dmgCmd.str().c_str() << std::endl
339 << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
340 return 0;
343 return 1;
346 //----------------------------------------------------------------------
347 int cmCPackPackageMakerGenerator::InitializeInternal()
349 cmCPackLogger(cmCPackLog::LOG_DEBUG,
350 "cmCPackPackageMakerGenerator::Initialize()" << std::endl);
351 this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
352 std::vector<std::string> path;
353 std::string pkgPath
354 = "/Developer/Applications/Utilities/PackageMaker.app/Contents";
355 std::string versionFile = pkgPath + "/version.plist";
356 if ( !cmSystemTools::FileExists(versionFile.c_str()) )
358 pkgPath = "/Developer/Applications/PackageMaker.app/Contents";
359 std::string newVersionFile = pkgPath + "/version.plist";
360 if ( !cmSystemTools::FileExists(newVersionFile.c_str()) )
362 cmCPackLogger(cmCPackLog::LOG_ERROR,
363 "Cannot find PackageMaker compiler version file: "
364 << versionFile.c_str() << " or " << newVersionFile.c_str()
365 << std::endl);
366 return 0;
368 versionFile = newVersionFile;
370 std::ifstream ifs(versionFile.c_str());
371 if ( !ifs )
373 cmCPackLogger(cmCPackLog::LOG_ERROR,
374 "Cannot open PackageMaker compiler version file" << std::endl);
375 return 0;
377 // Check the PackageMaker version
378 cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
379 cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
380 std::string line;
381 bool foundKey = false;
382 while ( cmSystemTools::GetLineFromStream(ifs, line) )
384 if ( rexKey.find(line) )
386 foundKey = true;
387 break;
390 if ( !foundKey )
392 cmCPackLogger(cmCPackLog::LOG_ERROR,
393 "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
394 "version file" << std::endl);
395 return 0;
397 if ( !cmSystemTools::GetLineFromStream(ifs, line) ||
398 !rexVersion.find(line) )
400 cmCPackLogger(cmCPackLog::LOG_ERROR,
401 "Problem reading the PackageMaker compiler version file: "
402 << versionFile.c_str() << std::endl);
403 return 0;
405 this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
406 if ( this->PackageMakerVersion < 1.0 )
408 cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher"
409 << std::endl);
410 return 0;
412 cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: "
413 << this->PackageMakerVersion << std::endl);
415 // Determine the package compatibility version. If it wasn't
416 // specified by the user, we define it based on which features the
417 // user requested.
418 const char *packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
419 if (packageCompat && *packageCompat)
421 this->PackageCompatibilityVersion = atof(packageCompat);
423 else if (this->GetOption("CPACK_DOWNLOAD_SITE"))
425 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5");
426 this->PackageCompatibilityVersion = 10.5;
428 else if (this->GetOption("CPACK_COMPONENTS_ALL"))
430 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4");
431 this->PackageCompatibilityVersion = 10.4;
433 else
435 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
436 this->PackageCompatibilityVersion = 10.3;
439 pkgPath += "/MacOS";
440 path.push_back(pkgPath);
441 pkgPath = cmSystemTools::FindProgram("PackageMaker", path, false);
442 if ( pkgPath.empty() )
444 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler"
445 << std::endl);
446 return 0;
448 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
449 pkgPath = cmSystemTools::FindProgram("hdiutil", path, false);
450 if ( pkgPath.empty() )
452 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
453 << std::endl);
454 return 0;
456 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
457 pkgPath.c_str());
459 return this->Superclass::InitializeInternal();
462 //----------------------------------------------------------------------
463 bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name,
464 const char* dirName)
466 std::string uname = cmSystemTools::UpperCase(name);
467 std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
468 const char* inFileName = this->GetOption(cpackVar.c_str());
469 if ( !inFileName )
471 cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str()
472 << " not specified. It should point to "
473 << (name ? name : "(NULL)")
474 << ".rtf, " << name
475 << ".html, or " << name << ".txt file" << std::endl);
476 return false;
478 if ( !cmSystemTools::FileExists(inFileName) )
480 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
481 << (name ? name : "(NULL)")
482 << " resource file: " << inFileName << std::endl);
483 return false;
485 std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
486 if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" )
488 cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: "
489 << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed."
490 << std::endl);
491 return false;
494 std::string destFileName = dirName;
495 destFileName += '/';
496 destFileName += name + ext;
498 // Set this so that distribution.dist gets the right name (without
499 // the path).
500 this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(),
501 (name + ext).c_str());
503 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
504 << (inFileName ? inFileName : "(NULL)")
505 << " to " << destFileName.c_str() << std::endl);
506 this->ConfigureFile(inFileName, destFileName.c_str());
507 return true;
510 bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
511 const char* outName)
513 if (!outName)
515 outName = name;
518 std::string inFName = "CPack.";
519 inFName += name;
520 inFName += ".in";
521 std::string inFileName = this->FindTemplate(inFName.c_str());
522 if ( inFileName.empty() )
524 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
525 << inFName << std::endl);
526 return false;
529 std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
530 destFileName += "/";
531 destFileName += outName;
533 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
534 << inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
535 this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
536 return true;
539 //----------------------------------------------------------------------
540 bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command,
541 const char *packageFile)
543 std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
544 tmpFile += "/PackageMakerOutput.log";
546 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
547 std::string output;
548 int retVal = 1;
549 bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0,
550 this->GeneratorVerbose, 0);
551 cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
552 << std::endl);
553 if ( !res || retVal )
555 cmGeneratedFileStream ofs(tmpFile.c_str());
556 ofs << "# Run command: " << command << std::endl
557 << "# Output:" << std::endl
558 << output.c_str() << std::endl;
559 cmCPackLogger(cmCPackLog::LOG_ERROR,
560 "Problem running PackageMaker command: " << command
561 << std::endl << "Please check " << tmpFile.c_str() << " for errors"
562 << std::endl);
563 return false;
565 // sometimes the command finishes but the directory is not yet
566 // created, so try 10 times to see if it shows up
567 int tries = 10;
568 while(tries > 0 &&
569 !cmSystemTools::FileExists(packageFile))
571 cmSystemTools::Delay(500);
572 tries--;
574 if(!cmSystemTools::FileExists(packageFile))
576 cmCPackLogger(
577 cmCPackLog::LOG_ERROR,
578 "Problem running PackageMaker command: " << command
579 << std::endl << "Package not created: " << packageFile
580 << std::endl);
581 return false;
584 return true;
587 //----------------------------------------------------------------------
588 std::string
589 cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component)
591 if (component.ArchiveFile.empty())
593 std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
594 packagesDir += ".dummy";
595 cmOStringStream out;
596 out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
597 << "-" << component.Name << ".pkg";
598 return out.str();
600 else
602 return component.ArchiveFile + ".pkg";
606 //----------------------------------------------------------------------
607 bool
608 cmCPackPackageMakerGenerator::
609 GenerateComponentPackage(const char *packageFile,
610 const char *packageDir,
611 const cmCPackComponent& component)
613 cmCPackLogger(cmCPackLog::LOG_OUTPUT,
614 "- Building component package: " <<
615 packageFile << std::endl);
617 // The command that will be used to run PackageMaker
618 cmOStringStream pkgCmd;
620 if (this->PackageCompatibilityVersion < 10.5 ||
621 this->PackageMakerVersion < 3.0)
623 // Create Description.plist and Info.plist files for normal Mac OS
624 // X packages, which work on Mac OS X 10.3 and newer.
625 std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
626 descriptionFile += '/' + component.Name + "-Description.plist";
627 std::ofstream out(descriptionFile.c_str());
628 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
629 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
630 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl
631 << "<plist version=\"1.4\">" << std::endl
632 << "<dict>" << std::endl
633 << " <key>IFPkgDescriptionTitle</key>" << std::endl
634 << " <string>" << component.DisplayName << "</string>" << std::endl
635 << " <key>IFPkgDescriptionVersion</key>" << std::endl
636 << " <string>" << this->GetOption("CPACK_PACKAGE_VERSION")
637 << "</string>" << std::endl
638 << " <key>IFPkgDescriptionDescription</key>" << std::endl
639 << " <string>" + this->EscapeForXML(component.Description)
640 << "</string>" << std::endl
641 << "</dict>" << std::endl
642 << "</plist>" << std::endl;
643 out.close();
645 // Create the Info.plist file for this component
646 std::string moduleVersionSuffix = ".";
647 moduleVersionSuffix += component.Name;
648 this->SetOption("CPACK_MODULE_VERSION_SUFFIX",
649 moduleVersionSuffix.c_str());
650 std::string infoFileName = component.Name;
651 infoFileName += "-Info.plist";
652 if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str()))
654 return false;
657 pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
658 << "\" -build -p \"" << packageFile << "\""
659 << " -f \"" << packageDir << "\""
660 << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
661 << "/" << infoFileName << "\""
662 << " -d \"" << descriptionFile << "\"";
664 else
666 // Create a "flat" package on Mac OS X 10.5 and newer. Flat
667 // packages are stored in a single file, rather than a directory
668 // like normal packages, and can be downloaded by the installer
669 // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create
670 // flat packages when the packages will be downloaded on the fly.
671 std::string pkgId = "com.";
672 pkgId += this->GetOption("CPACK_PACKAGE_VENDOR");
673 pkgId += '.';
674 pkgId += this->GetOption("CPACK_PACKAGE_NAME");
675 pkgId += '.';
676 pkgId += component.Name;
678 pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
679 << "\" --root \"" << packageDir << "\""
680 << " --id " << pkgId
681 << " --target " << this->GetOption("CPACK_OSX_PACKAGE_VERSION")
682 << " --out \"" << packageFile << "\"";
685 // Run PackageMaker
686 return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
689 //----------------------------------------------------------------------
690 void
691 cmCPackPackageMakerGenerator::
692 WriteDistributionFile(const char* metapackageFile)
694 std::string distributionTemplate
695 = this->FindTemplate("CPack.distribution.dist.in");
696 if ( distributionTemplate.empty() )
698 cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
699 << distributionTemplate << std::endl);
700 return;
703 std::string distributionFile = metapackageFile;
704 distributionFile += "/Contents/distribution.dist";
706 // Create the choice outline, which provides a tree-based view of
707 // the components in their groups.
708 cmOStringStream choiceOut;
709 choiceOut << "<choices-outline>" << std::endl;
711 // Emit the outline for the groups
712 std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
713 for (groupIt = this->ComponentGroups.begin();
714 groupIt != this->ComponentGroups.end();
715 ++groupIt)
717 if (groupIt->second.ParentGroup == 0)
719 CreateChoiceOutline(groupIt->second, choiceOut);
723 // Emit the outline for the non-grouped components
724 std::map<std::string, cmCPackComponent>::iterator compIt;
725 for (compIt = this->Components.begin(); compIt != this->Components.end();
726 ++compIt)
728 if (!compIt->second.Group)
730 choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>"
731 << std::endl;
734 choiceOut << "</choices-outline>" << std::endl;
736 // Create the actual choices
737 for (groupIt = this->ComponentGroups.begin();
738 groupIt != this->ComponentGroups.end();
739 ++groupIt)
741 CreateChoice(groupIt->second, choiceOut);
743 for (compIt = this->Components.begin(); compIt != this->Components.end();
744 ++compIt)
746 CreateChoice(compIt->second, choiceOut);
748 this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
750 // Create the distribution.dist file in the metapackage to turn it
751 // into a distribution package.
752 this->ConfigureFile(distributionTemplate.c_str(),
753 distributionFile.c_str());
756 //----------------------------------------------------------------------
757 void
758 cmCPackPackageMakerGenerator::
759 CreateChoiceOutline(const cmCPackComponentGroup& group, cmOStringStream& out)
761 out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
762 std::vector<cmCPackComponentGroup*>::const_iterator groupIt;
763 for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end();
764 ++groupIt)
766 CreateChoiceOutline(**groupIt, out);
769 std::vector<cmCPackComponent*>::const_iterator compIt;
770 for (compIt = group.Components.begin(); compIt != group.Components.end();
771 ++compIt)
773 out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>"
774 << std::endl;
776 out << "</line>" << std::endl;
779 //----------------------------------------------------------------------
780 void
781 cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponentGroup& group,
782 cmOStringStream& out)
784 out << "<choice id=\"" << group.Name << "Choice\" "
785 << "title=\"" << group.DisplayName << "\" "
786 << "start_selected=\"true\" "
787 << "start_enabled=\"true\" "
788 << "start_visible=\"true\" ";
789 if (!group.Description.empty())
791 out << "description=\"" << EscapeForXML(group.Description)
792 << "\"";
794 out << "></choice>" << std::endl;
797 //----------------------------------------------------------------------
798 void
799 cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponent& component,
800 cmOStringStream& out)
802 std::string packageId = "com.";
803 packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
804 packageId += '.';
805 packageId += this->GetOption("CPACK_PACKAGE_NAME");
806 packageId += '.';
807 packageId += component.Name;
809 out << "<choice id=\"" << component.Name << "Choice\" "
810 << "title=\"" << component.DisplayName << "\" "
811 << "start_selected=\""
812 << (component.IsDisabledByDefault &&
813 !component.IsRequired? "false" : "true")
814 << "\" "
815 << "start_enabled=\""
816 << (component.IsRequired? "false" : "true")
817 << "\" "
818 << "start_visible=\"" << (component.IsHidden? "false" : "true") << "\" ";
819 if (!component.Description.empty())
821 out << "description=\"" << EscapeForXML(component.Description)
822 << "\" ";
824 if (!component.Dependencies.empty() ||
825 !component.ReverseDependencies.empty())
827 // The "selected" expression is evaluated each time any choice is
828 // selected, for all choices *except* the one that the user
829 // selected. A component is marked selected if it has been
830 // selected (my.choice.selected in Javascript) and all of the
831 // components it depends on have been selected (transitively) or
832 // if any of the components that depend on it have been selected
833 // (transitively). Assume that we have components A, B, C, D, and
834 // E, where each component depends on the previous component (B
835 // depends on A, C depends on B, D depends on C, and E depends on
836 // D). The expression we build for the component C will be
837 // my.choice.selected && B && A || D || E
838 // This way, selecting C will automatically select everything it depends
839 // on (B and A), while selecting something that depends on C--either D
840 // or E--will automatically cause C to get selected.
841 out << "selected=\"my.choice.selected";
842 std::set<const cmCPackComponent *> visited;
843 AddDependencyAttributes(component, visited, out);
844 visited.clear();
845 AddReverseDependencyAttributes(component, visited, out);
846 out << "\"";
848 out << ">" << std::endl;
849 out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl;
850 out << "</choice>" << std::endl;
852 // Create a description of the package associated with this
853 // component.
854 std::string relativePackageLocation = "Contents/Packages/";
855 relativePackageLocation += this->GetPackageName(component);
857 // Determine the installed size of the package.
858 std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
859 dirName += '/';
860 dirName += component.Name;
861 unsigned long installedSize
862 = component.GetInstalledSizeInKbytes(dirName.c_str());
864 out << "<pkg-ref id=\"" << packageId << "\" "
865 << "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
866 << "installKBytes=\"" << installedSize << "\" "
867 << "auth=\"Admin\" onConclusion=\"None\">";
868 if (component.IsDownloaded)
870 out << this->GetOption("CPACK_DOWNLOAD_SITE")
871 << this->GetPackageName(component);
873 else
875 out << "file:./" << relativePackageLocation;
877 out << "</pkg-ref>" << std::endl;
880 //----------------------------------------------------------------------
881 void
882 cmCPackPackageMakerGenerator::
883 AddDependencyAttributes(const cmCPackComponent& component,
884 std::set<const cmCPackComponent *>& visited,
885 cmOStringStream& out)
887 if (visited.find(&component) != visited.end())
889 return;
891 visited.insert(&component);
893 std::vector<cmCPackComponent *>::const_iterator dependIt;
894 for (dependIt = component.Dependencies.begin();
895 dependIt != component.Dependencies.end();
896 ++dependIt)
898 out << " &amp;&amp; choices['" <<
899 (*dependIt)->Name << "Choice'].selected";
900 AddDependencyAttributes(**dependIt, visited, out);
904 //----------------------------------------------------------------------
905 void
906 cmCPackPackageMakerGenerator::
907 AddReverseDependencyAttributes(const cmCPackComponent& component,
908 std::set<const cmCPackComponent *>& visited,
909 cmOStringStream& out)
911 if (visited.find(&component) != visited.end())
913 return;
915 visited.insert(&component);
917 std::vector<cmCPackComponent *>::const_iterator dependIt;
918 for (dependIt = component.ReverseDependencies.begin();
919 dependIt != component.ReverseDependencies.end();
920 ++dependIt)
922 out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
923 AddReverseDependencyAttributes(**dependIt, visited, out);
927 //----------------------------------------------------------------------
928 std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str)
930 cmSystemTools::ReplaceString(str, "&", "&amp;");
931 cmSystemTools::ReplaceString(str, "<", "&lt;");
932 cmSystemTools::ReplaceString(str, ">", "&gt;");
933 cmSystemTools::ReplaceString(str, "\"", "&quot;");
934 return str;