1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCPackPackageMakerGenerator.cxx,v $
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"
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
,
54 std::string dst
= resdir
;
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() <<
66 //----------------------------------------------------------------------
67 int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName
,
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";
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
);
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
104 resDir
+= "/Resources";
105 if ( !cmsys::SystemTools::MakeDirectory(resDir
.c_str()))
107 cmCPackLogger(cmCPackLog::LOG_ERROR
,
108 "unable to create package subdirectory " << resDir
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
127 if ( !cmsys::SystemTools::MakeDirectory(preflightDirName
.c_str()))
129 cmCPackLogger(cmCPackLog::LOG_ERROR
,
130 "Problem creating installer directory: "
131 << preflightDirName
.c_str() << std::endl
);
137 if ( !cmsys::SystemTools::MakeDirectory(postflightDirName
.c_str()))
139 cmCPackLogger(cmCPackLog::LOG_ERROR
,
140 "Problem creating installer directory: "
141 << postflightDirName
.c_str() << std::endl
);
145 // if preflight, postflight, or postupgrade are set
146 // then copy them into the resource directory and make
150 this->CopyInstallScript(resDir
.c_str(),
156 this->CopyInstallScript(resDir
.c_str(),
162 this->CopyInstallScript(resDir
.c_str(),
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
);
180 // Create the directory where downloaded component packages will
182 const char* userUploadDirectory
=
183 this->GetOption("CPACK_UPLOAD_DIRECTORY");
184 std::string uploadDirectory
;
185 if (userUploadDirectory
&& *userUploadDirectory
)
187 uploadDirectory
= userUploadDirectory
;
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();
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
);
222 else if (!warnedAboutDownloadCompatibility
)
224 if (this->PackageCompatibilityVersion
< 10.5)
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."
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."
243 warnedAboutDownloadCompatibility
= true;
247 if (packageFile
.empty())
249 // Build this package within the overall distribution
251 packageFile
= basePackageDir
;
253 // We're not downloading this component, even if the user
255 compIt
->second
.IsDownloaded
= false;
259 packageFile
+= GetPackageName(compIt
->second
);
261 std::string packageDir
= toplevel
;
263 packageDir
+= compIt
->first
;
264 if (!this->GenerateComponentPackage(packageFile
.c_str(),
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"
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");
298 pkgCmd
<< " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
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 )
311 if (!RunPackageMaker(pkgCmd
.str().c_str(), packageDirFileName
.c_str()))
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
<< "\"";
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
);
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
;
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()
368 versionFile
= newVersionFile
;
370 std::ifstream
ifs(versionFile
.c_str());
373 cmCPackLogger(cmCPackLog::LOG_ERROR
,
374 "Cannot open PackageMaker compiler version file" << std::endl
);
377 // Check the PackageMaker version
378 cmsys::RegularExpression
rexKey("<key>CFBundleShortVersionString</key>");
379 cmsys::RegularExpression
rexVersion("<string>([0-9]+.[0-9.]+)</string>");
381 bool foundKey
= false;
382 while ( cmSystemTools::GetLineFromStream(ifs
, line
) )
384 if ( rexKey
.find(line
) )
392 cmCPackLogger(cmCPackLog::LOG_ERROR
,
393 "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
394 "version file" << std::endl
);
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
);
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"
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
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;
435 this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
436 this->PackageCompatibilityVersion
= 10.3;
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"
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"
456 this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
459 return this->Superclass::InitializeInternal();
462 //----------------------------------------------------------------------
463 bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name
,
466 std::string uname
= cmSystemTools::UpperCase(name
);
467 std::string cpackVar
= "CPACK_RESOURCE_FILE_" + uname
;
468 const char* inFileName
= this->GetOption(cpackVar
.c_str());
471 cmCPackLogger(cmCPackLog::LOG_ERROR
, "CPack option: " << cpackVar
.c_str()
472 << " not specified. It should point to "
473 << (name
? name
: "(NULL)")
475 << ".html, or " << name
<< ".txt file" << std::endl
);
478 if ( !cmSystemTools::FileExists(inFileName
) )
480 cmCPackLogger(cmCPackLog::LOG_ERROR
, "Cannot find "
481 << (name
? name
: "(NULL)")
482 << " resource file: " << inFileName
<< std::endl
);
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."
494 std::string destFileName
= dirName
;
496 destFileName
+= name
+ ext
;
498 // Set this so that distribution.dist gets the right name (without
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());
510 bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name
,
518 std::string inFName
= "CPack.";
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
);
529 std::string destFileName
= this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
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());
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
);
549 bool res
= cmSystemTools::RunSingleCommand(command
, &output
, &retVal
, 0,
550 this->GeneratorVerbose
, 0);
551 cmCPackLogger(cmCPackLog::LOG_VERBOSE
, "Done running package maker"
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"
565 // sometimes the command finishes but the directory is not yet
566 // created, so try 10 times to see if it shows up
569 !cmSystemTools::FileExists(packageFile
))
571 cmSystemTools::Delay(500);
574 if(!cmSystemTools::FileExists(packageFile
))
577 cmCPackLog::LOG_ERROR
,
578 "Problem running PackageMaker command: " << command
579 << std::endl
<< "Package not created: " << packageFile
587 //----------------------------------------------------------------------
589 cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent
& component
)
591 if (component
.ArchiveFile
.empty())
593 std::string packagesDir
= this->GetOption("CPACK_TEMPORARY_DIRECTORY");
594 packagesDir
+= ".dummy";
596 out
<< cmSystemTools::GetFilenameWithoutLastExtension(packagesDir
)
597 << "-" << component
.Name
<< ".pkg";
602 return component
.ArchiveFile
+ ".pkg";
606 //----------------------------------------------------------------------
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
;
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()))
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
<< "\"";
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");
674 pkgId
+= this->GetOption("CPACK_PACKAGE_NAME");
676 pkgId
+= component
.Name
;
678 pkgCmd
<< "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
679 << "\" --root \"" << packageDir
<< "\""
681 << " --target " << this->GetOption("CPACK_OSX_PACKAGE_VERSION")
682 << " --out \"" << packageFile
<< "\"";
686 return RunPackageMaker(pkgCmd
.str().c_str(), packageFile
);
689 //----------------------------------------------------------------------
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
);
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();
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();
728 if (!compIt
->second
.Group
)
730 choiceOut
<< "<line choice=\"" << compIt
->first
<< "Choice\"></line>"
734 choiceOut
<< "</choices-outline>" << std::endl
;
736 // Create the actual choices
737 for (groupIt
= this->ComponentGroups
.begin();
738 groupIt
!= this->ComponentGroups
.end();
741 CreateChoice(groupIt
->second
, choiceOut
);
743 for (compIt
= this->Components
.begin(); compIt
!= this->Components
.end();
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 //----------------------------------------------------------------------
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();
766 CreateChoiceOutline(**groupIt
, out
);
769 std::vector
<cmCPackComponent
*>::const_iterator compIt
;
770 for (compIt
= group
.Components
.begin(); compIt
!= group
.Components
.end();
773 out
<< " <line choice=\"" << (*compIt
)->Name
<< "Choice\"></line>"
776 out
<< "</line>" << std::endl
;
779 //----------------------------------------------------------------------
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
)
794 out
<< "></choice>" << std::endl
;
797 //----------------------------------------------------------------------
799 cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponent
& component
,
800 cmOStringStream
& out
)
802 std::string packageId
= "com.";
803 packageId
+= this->GetOption("CPACK_PACKAGE_VENDOR");
805 packageId
+= this->GetOption("CPACK_PACKAGE_NAME");
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")
815 << "start_enabled=\""
816 << (component
.IsRequired
? "false" : "true")
818 << "start_visible=\"" << (component
.IsHidden
? "false" : "true") << "\" ";
819 if (!component
.Description
.empty())
821 out
<< "description=\"" << EscapeForXML(component
.Description
)
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
);
845 AddReverseDependencyAttributes(component
, visited
, 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
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");
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
);
875 out
<< "file:./" << relativePackageLocation
;
877 out
<< "</pkg-ref>" << std::endl
;
880 //----------------------------------------------------------------------
882 cmCPackPackageMakerGenerator::
883 AddDependencyAttributes(const cmCPackComponent
& component
,
884 std::set
<const cmCPackComponent
*>& visited
,
885 cmOStringStream
& out
)
887 if (visited
.find(&component
) != visited
.end())
891 visited
.insert(&component
);
893 std::vector
<cmCPackComponent
*>::const_iterator dependIt
;
894 for (dependIt
= component
.Dependencies
.begin();
895 dependIt
!= component
.Dependencies
.end();
898 out
<< " && choices['" <<
899 (*dependIt
)->Name
<< "Choice'].selected";
900 AddDependencyAttributes(**dependIt
, visited
, out
);
904 //----------------------------------------------------------------------
906 cmCPackPackageMakerGenerator::
907 AddReverseDependencyAttributes(const cmCPackComponent
& component
,
908 std::set
<const cmCPackComponent
*>& visited
,
909 cmOStringStream
& out
)
911 if (visited
.find(&component
) != visited
.end())
915 visited
.insert(&component
);
917 std::vector
<cmCPackComponent
*>::const_iterator dependIt
;
918 for (dependIt
= component
.ReverseDependencies
.begin();
919 dependIt
!= component
.ReverseDependencies
.end();
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
, "&", "&");
931 cmSystemTools::ReplaceString(str
, "<", "<");
932 cmSystemTools::ReplaceString(str
, ">", ">");
933 cmSystemTools::ReplaceString(str
, "\"", """);