1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmCPackPackageMakerGenerator.h"
4 
5 #include <cassert>
6 #include <cstdio>
7 #include <cstdlib>
8 #include <map>
9 #include <sstream>
10 #include <string>
11 
12 #include "cmsys/FStream.hxx"
13 #include "cmsys/RegularExpression.hxx"
14 
15 #include "cmCPackComponentGroup.h"
16 #include "cmCPackLog.h"
17 #include "cmDuration.h"
18 #include "cmGeneratedFileStream.h"
19 #include "cmStringAlgorithms.h"
20 #include "cmSystemTools.h"
21 #include "cmValue.h"
22 #include "cmXMLWriter.h"
23 
getVersion(unsigned int major,unsigned int minor)24 static inline unsigned int getVersion(unsigned int major, unsigned int minor)
25 {
26   assert(major < 256 && minor < 256);
27   return ((major & 0xFF) << 16 | minor);
28 }
29 
cmCPackPackageMakerGenerator()30 cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
31 {
32   this->PackageMakerVersion = 0.0;
33   this->PackageCompatibilityVersion = getVersion(10, 4);
34 }
35 
36 cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator() = default;
37 
SupportsComponentInstallation() const38 bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
39 {
40   return this->PackageCompatibilityVersion >= getVersion(10, 4);
41 }
42 
PackageFiles()43 int cmCPackPackageMakerGenerator::PackageFiles()
44 {
45   // TODO: Use toplevel
46   //       It is used! Is this an obsolete comment?
47 
48   std::string resDir; // Where this package's resources will go.
49   std::string packageDirFileName =
50     this->GetOption("CPACK_TEMPORARY_DIRECTORY");
51   if (this->Components.empty()) {
52     packageDirFileName += ".pkg";
53     resDir =
54       cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/Resources");
55   } else {
56     packageDirFileName += ".mpkg";
57     if (!cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str())) {
58       cmCPackLogger(cmCPackLog::LOG_ERROR,
59                     "unable to create package directory " << packageDirFileName
60                                                           << std::endl);
61       return 0;
62     }
63 
64     resDir = cmStrCat(packageDirFileName, "/Contents");
65     if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) {
66       cmCPackLogger(cmCPackLog::LOG_ERROR,
67                     "unable to create package subdirectory " << resDir
68                                                              << std::endl);
69       return 0;
70     }
71 
72     resDir += "/Resources";
73     if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) {
74       cmCPackLogger(cmCPackLog::LOG_ERROR,
75                     "unable to create package subdirectory " << resDir
76                                                              << std::endl);
77       return 0;
78     }
79 
80     resDir += "/en.lproj";
81   }
82 
83   cmValue preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
84   cmValue postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
85   cmValue postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
86 
87   if (this->Components.empty()) {
88     // Create directory structure
89     std::string preflightDirName = resDir + "/PreFlight";
90     std::string postflightDirName = resDir + "/PostFlight";
91     // if preflight or postflight scripts not there create directories
92     // of the same name, I think this makes it work
93     if (!preflight) {
94       if (!cmsys::SystemTools::MakeDirectory(preflightDirName.c_str())) {
95         cmCPackLogger(cmCPackLog::LOG_ERROR,
96                       "Problem creating installer directory: "
97                         << preflightDirName << std::endl);
98         return 0;
99       }
100     }
101     if (!postflight) {
102       if (!cmsys::SystemTools::MakeDirectory(postflightDirName.c_str())) {
103         cmCPackLogger(cmCPackLog::LOG_ERROR,
104                       "Problem creating installer directory: "
105                         << postflightDirName << std::endl);
106         return 0;
107       }
108     }
109     // if preflight, postflight, or postupgrade are set
110     // then copy them into the resource directory and make
111     // them executable
112     if (preflight) {
113       this->CopyInstallScript(resDir, preflight, "preflight");
114     }
115     if (postflight) {
116       this->CopyInstallScript(resDir, postflight, "postflight");
117     }
118     if (postupgrade) {
119       this->CopyInstallScript(resDir, postupgrade, "postupgrade");
120     }
121   } else if (postflight) {
122     // create a postflight component to house the script
123     this->PostFlightComponent.Name = "PostFlight";
124     this->PostFlightComponent.DisplayName = "PostFlight";
125     this->PostFlightComponent.Description = "PostFlight";
126     this->PostFlightComponent.IsHidden = true;
127 
128     // empty directory for pkg contents
129     std::string packageDir = toplevel + "/" + PostFlightComponent.Name;
130     if (!cmsys::SystemTools::MakeDirectory(packageDir.c_str())) {
131       cmCPackLogger(cmCPackLog::LOG_ERROR,
132                     "Problem creating component packages directory: "
133                       << packageDir << std::endl);
134       return 0;
135     }
136 
137     // create package
138     std::string packageFileDir = packageDirFileName + "/Contents/Packages/";
139     if (!cmsys::SystemTools::MakeDirectory(packageFileDir.c_str())) {
140       cmCPackLogger(
141         cmCPackLog::LOG_ERROR,
142         "Problem creating component PostFlight Packages directory: "
143           << packageFileDir << std::endl);
144       return 0;
145     }
146     std::string packageFile =
147       packageFileDir + this->GetPackageName(PostFlightComponent);
148     if (!this->GenerateComponentPackage(
149           packageFile.c_str(), packageDir.c_str(), PostFlightComponent)) {
150       return 0;
151     }
152 
153     // copy postflight script into resource directory of .pkg
154     std::string resourceDir = packageFile + "/Contents/Resources";
155     this->CopyInstallScript(resourceDir, postflight, "postflight");
156   }
157 
158   if (!this->Components.empty()) {
159     // Create the directory where component packages will be built.
160     std::string basePackageDir =
161       cmStrCat(packageDirFileName, "/Contents/Packages");
162     if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) {
163       cmCPackLogger(cmCPackLog::LOG_ERROR,
164                     "Problem creating component packages directory: "
165                       << basePackageDir << std::endl);
166       return 0;
167     }
168 
169     // Create the directory where downloaded component packages will
170     // be placed.
171     cmValue userUploadDirectory = this->GetOption("CPACK_UPLOAD_DIRECTORY");
172     std::string uploadDirectory;
173     if (userUploadDirectory && !userUploadDirectory->empty()) {
174       uploadDirectory = userUploadDirectory;
175     } else {
176       uploadDirectory =
177         cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads");
178     }
179 
180     // Create packages for each component
181     bool warnedAboutDownloadCompatibility = false;
182 
183     std::map<std::string, cmCPackComponent>::iterator compIt;
184     for (compIt = this->Components.begin(); compIt != this->Components.end();
185          ++compIt) {
186       std::string packageFile;
187       if (compIt->second.IsDownloaded) {
188         if (this->PackageCompatibilityVersion >= getVersion(10, 5) &&
189             this->PackageMakerVersion >= 3.0) {
190           // Build this package within the upload directory.
191           packageFile = uploadDirectory;
192 
193           if (!cmSystemTools::FileExists(uploadDirectory.c_str())) {
194             if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) {
195               cmCPackLogger(cmCPackLog::LOG_ERROR,
196                             "Unable to create package upload directory "
197                               << uploadDirectory << std::endl);
198               return 0;
199             }
200           }
201         } else if (!warnedAboutDownloadCompatibility) {
202           if (this->PackageCompatibilityVersion < getVersion(10, 5)) {
203             cmCPackLogger(
204               cmCPackLog::LOG_WARNING,
205               "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 "
206               "or greater enable downloaded packages. CPack will build a "
207               "non-downloaded package."
208                 << std::endl);
209           }
210 
211           if (this->PackageMakerVersion < 3) {
212             cmCPackLogger(cmCPackLog::LOG_WARNING,
213                           "CPack warning: unable to build downloaded "
214                           "packages with PackageMaker versions prior "
215                           "to 3.0. CPack will build a non-downloaded package."
216                             << std::endl);
217           }
218 
219           warnedAboutDownloadCompatibility = true;
220         }
221       }
222 
223       if (packageFile.empty()) {
224         // Build this package within the overall distribution
225         // metapackage.
226         packageFile = basePackageDir;
227 
228         // We're not downloading this component, even if the user
229         // requested it.
230         compIt->second.IsDownloaded = false;
231       }
232 
233       packageFile += '/';
234       packageFile += GetPackageName(compIt->second);
235 
236       std::string packageDir = cmStrCat(toplevel, '/', compIt->first);
237       if (!this->GenerateComponentPackage(
238             packageFile.c_str(), packageDir.c_str(), compIt->second)) {
239         return 0;
240       }
241     }
242   }
243   this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
244 
245   // Copy or create all of the resource files we need.
246   if (!this->CopyCreateResourceFile("License", resDir) ||
247       !this->CopyCreateResourceFile("ReadMe", resDir) ||
248       !this->CopyCreateResourceFile("Welcome", resDir) ||
249       !this->CopyResourcePlistFile("Info.plist") ||
250       !this->CopyResourcePlistFile("Description.plist")) {
251     cmCPackLogger(cmCPackLog::LOG_ERROR,
252                   "Problem copying the resource files" << std::endl);
253     return 0;
254   }
255 
256   if (this->Components.empty()) {
257     // Use PackageMaker to build the package.
258     std::ostringstream pkgCmd;
259     pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
260            << "\" -build -p \"" << packageDirFileName << "\"";
261     if (this->Components.empty()) {
262       pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
263     } else {
264       pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
265              << "/packages/";
266     }
267     pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
268            << "/Resources\" -i \""
269            << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
270            << "/Info.plist\" -d \""
271            << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
272            << "/Description.plist\"";
273     if (this->PackageMakerVersion > 2.0) {
274       pkgCmd << " -v";
275     }
276     if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str())) {
277       return 0;
278     }
279   } else {
280     // We have built the package in place. Generate the
281     // distribution.dist file to describe it for the installer.
282     WriteDistributionFile(packageDirFileName.c_str(), "PACKAGEMAKER");
283   }
284 
285   std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
286                                  "/hdiutilOutput.log");
287   std::ostringstream dmgCmd;
288   dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
289          << "\" create -ov -fs HFS+ -format UDZO -srcfolder \""
290          << packageDirFileName << "\" \"" << packageFileNames[0] << "\"";
291   std::string output;
292   int retVal = 1;
293   int numTries = 10;
294   bool res = false;
295   while (numTries > 0) {
296     res = cmSystemTools::RunSingleCommand(
297       dmgCmd.str(), &output, &output, &retVal, nullptr, this->GeneratorVerbose,
298       cmDuration::zero());
299     if (res && !retVal) {
300       numTries = -1;
301       break;
302     }
303     cmSystemTools::Delay(500);
304     numTries--;
305   }
306   if (!res || retVal) {
307     cmGeneratedFileStream ofs(tmpFile);
308     ofs << "# Run command: " << dmgCmd.str() << std::endl
309         << "# Output:" << std::endl
310         << output << std::endl;
311     cmCPackLogger(cmCPackLog::LOG_ERROR,
312                   "Problem running hdiutil command: "
313                     << dmgCmd.str() << std::endl
314                     << "Please check " << tmpFile << " for errors"
315                     << std::endl);
316     return 0;
317   }
318 
319   return 1;
320 }
321 
InitializeInternal()322 int cmCPackPackageMakerGenerator::InitializeInternal()
323 {
324   cmCPackLogger(cmCPackLog::LOG_WARNING,
325                 "The PackageMaker generator is deprecated "
326                 "and will be removed in a future version.\n");
327   this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");
328 
329   // Starting with Xcode 4.3, PackageMaker is a separate app, and you
330   // can put it anywhere you want. So... use a variable for its location.
331   // People who put it in unexpected places can use the variable to tell
332   // us where it is.
333   //
334   // Use the following locations, in "most recent installation" order,
335   // to search for the PackageMaker app. Assume people who copy it into
336   // the new Xcode 4.3 app in "/Applications" will copy it into the nested
337   // Applications folder inside the Xcode bundle itself. Or directly in
338   // the "/Applications" directory.
339   //
340   // If found, save result in the CPACK_INSTALLER_PROGRAM variable.
341 
342   std::vector<std::string> paths;
343   paths.emplace_back("/Applications/Xcode.app/Contents/Applications"
344                      "/PackageMaker.app/Contents/MacOS");
345   paths.emplace_back("/Applications/Utilities"
346                      "/PackageMaker.app/Contents/MacOS");
347   paths.emplace_back("/Applications"
348                      "/PackageMaker.app/Contents/MacOS");
349   paths.emplace_back("/Developer/Applications/Utilities"
350                      "/PackageMaker.app/Contents/MacOS");
351   paths.emplace_back("/Developer/Applications"
352                      "/PackageMaker.app/Contents/MacOS");
353 
354   std::string pkgPath;
355   cmValue inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
356   if (inst_program && !inst_program->empty()) {
357     pkgPath = inst_program;
358   } else {
359     pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false);
360     if (pkgPath.empty()) {
361       cmCPackLogger(cmCPackLog::LOG_ERROR,
362                     "Cannot find PackageMaker compiler" << std::endl);
363       return 0;
364     }
365     this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath);
366   }
367 
368   // Get path to the real PackageMaker, not a symlink:
369   pkgPath = cmSystemTools::GetRealPath(pkgPath);
370   // Up from there to find the version.plist file in the "Contents" dir:
371   std::string contents_dir;
372   contents_dir = cmSystemTools::GetFilenamePath(pkgPath);
373   contents_dir = cmSystemTools::GetFilenamePath(contents_dir);
374 
375   std::string versionFile = contents_dir + "/version.plist";
376 
377   if (!cmSystemTools::FileExists(versionFile.c_str())) {
378     cmCPackLogger(cmCPackLog::LOG_ERROR,
379                   "Cannot find PackageMaker compiler version file: "
380                     << versionFile << std::endl);
381     return 0;
382   }
383 
384   cmsys::ifstream ifs(versionFile.c_str());
385   if (!ifs) {
386     cmCPackLogger(cmCPackLog::LOG_ERROR,
387                   "Cannot open PackageMaker compiler version file"
388                     << std::endl);
389     return 0;
390   }
391 
392   // Check the PackageMaker version
393   cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
394   cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
395   std::string line;
396   bool foundKey = false;
397   while (cmSystemTools::GetLineFromStream(ifs, line)) {
398     if (rexKey.find(line)) {
399       foundKey = true;
400       break;
401     }
402   }
403   if (!foundKey) {
404     cmCPackLogger(
405       cmCPackLog::LOG_ERROR,
406       "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
407       "version file"
408         << std::endl);
409     return 0;
410   }
411   if (!cmSystemTools::GetLineFromStream(ifs, line) || !rexVersion.find(line)) {
412     cmCPackLogger(cmCPackLog::LOG_ERROR,
413                   "Problem reading the PackageMaker compiler version file: "
414                     << versionFile << std::endl);
415     return 0;
416   }
417   this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
418   if (this->PackageMakerVersion < 1.0) {
419     cmCPackLogger(cmCPackLog::LOG_ERROR,
420                   "Require PackageMaker 1.0 or higher" << std::endl);
421     return 0;
422   }
423   cmCPackLogger(cmCPackLog::LOG_DEBUG,
424                 "PackageMaker version is: " << this->PackageMakerVersion
425                                             << std::endl);
426 
427   // Determine the package compatibility version. If it wasn't
428   // specified by the user, we define it based on which features the
429   // user requested.
430   cmValue packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
431   if (packageCompat && !packageCompat->empty()) {
432     unsigned int majorVersion = 10;
433     unsigned int minorVersion = 5;
434     int res =
435       sscanf(packageCompat->c_str(), "%u.%u", &majorVersion, &minorVersion);
436     if (res == 2) {
437       this->PackageCompatibilityVersion =
438         getVersion(majorVersion, minorVersion);
439     }
440   } else if (this->GetOption("CPACK_DOWNLOAD_SITE")) {
441     this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5");
442     this->PackageCompatibilityVersion = getVersion(10, 5);
443   } else if (this->GetOption("CPACK_COMPONENTS_ALL")) {
444     this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4");
445     this->PackageCompatibilityVersion = getVersion(10, 4);
446   } else {
447     this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
448     this->PackageCompatibilityVersion = getVersion(10, 3);
449   }
450 
451   std::vector<std::string> no_paths;
452   pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false);
453   if (pkgPath.empty()) {
454     cmCPackLogger(cmCPackLog::LOG_ERROR,
455                   "Cannot find hdiutil compiler" << std::endl);
456     return 0;
457   }
458   this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", pkgPath);
459 
460   return this->Superclass::InitializeInternal();
461 }
462 
RunPackageMaker(const char * command,const char * packageFile)463 bool cmCPackPackageMakerGenerator::RunPackageMaker(const char* command,
464                                                    const char* packageFile)
465 {
466   std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
467                                  "/PackageMakerOutput.log");
468 
469   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
470   std::string output;
471   int retVal = 1;
472   bool res = cmSystemTools::RunSingleCommand(
473     command, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
474     cmDuration::zero());
475   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
476                 "Done running package maker" << std::endl);
477   if (!res || retVal) {
478     cmGeneratedFileStream ofs(tmpFile);
479     ofs << "# Run command: " << command << std::endl
480         << "# Output:" << std::endl
481         << output << std::endl;
482     cmCPackLogger(cmCPackLog::LOG_ERROR,
483                   "Problem running PackageMaker command: "
484                     << command << std::endl
485                     << "Please check " << tmpFile << " for errors"
486                     << std::endl);
487     return false;
488   }
489   // sometimes the command finishes but the directory is not yet
490   // created, so try 10 times to see if it shows up
491   int tries = 10;
492   while (tries > 0 && !cmSystemTools::FileExists(packageFile)) {
493     cmSystemTools::Delay(500);
494     tries--;
495   }
496   if (!cmSystemTools::FileExists(packageFile)) {
497     cmCPackLogger(cmCPackLog::LOG_ERROR,
498                   "Problem running PackageMaker command: "
499                     << command << std::endl
500                     << "Package not created: " << packageFile << std::endl);
501     return false;
502   }
503 
504   return true;
505 }
506 
GenerateComponentPackage(const char * packageFile,const char * packageDir,const cmCPackComponent & component)507 bool cmCPackPackageMakerGenerator::GenerateComponentPackage(
508   const char* packageFile, const char* packageDir,
509   const cmCPackComponent& component)
510 {
511   cmCPackLogger(cmCPackLog::LOG_OUTPUT,
512                 "-   Building component package: " << packageFile
513                                                    << std::endl);
514 
515   // The command that will be used to run PackageMaker
516   std::ostringstream pkgCmd;
517 
518   if (this->PackageCompatibilityVersion < getVersion(10, 5) ||
519       this->PackageMakerVersion < 3.0) {
520     // Create Description.plist and Info.plist files for normal Mac OS
521     // X packages, which work on Mac OS X 10.3 and newer.
522     std::string descriptionFile =
523       cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
524                component.Name, "-Description.plist");
525     cmsys::ofstream out(descriptionFile.c_str());
526     cmXMLWriter xout(out);
527     xout.StartDocument();
528     xout.Doctype("plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
529                  "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
530     xout.StartElement("plist");
531     xout.Attribute("version", "1.4");
532     xout.StartElement("dict");
533     xout.Element("key", "IFPkgDescriptionTitle");
534     xout.Element("string", component.DisplayName);
535     xout.Element("key", "IFPkgDescriptionVersion");
536     xout.Element("string", this->GetOption("CPACK_PACKAGE_VERSION"));
537     xout.Element("key", "IFPkgDescriptionDescription");
538     xout.Element("string", component.Description);
539     xout.EndElement(); // dict
540     xout.EndElement(); // plist
541     xout.EndDocument();
542     out.close();
543 
544     // Create the Info.plist file for this component
545     std::string moduleVersionSuffix = cmStrCat('.', component.Name);
546     this->SetOption("CPACK_MODULE_VERSION_SUFFIX", moduleVersionSuffix);
547     std::string infoFileName = cmStrCat(component.Name, "-Info.plist");
548     if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str())) {
549       return false;
550     }
551 
552     pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
553            << "\" -build -p \"" << packageFile << "\""
554            << " -f \"" << packageDir << "\""
555            << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/"
556            << infoFileName << "\""
557            << " -d \"" << descriptionFile << "\"";
558   } else {
559     // Create a "flat" package on Mac OS X 10.5 and newer. Flat
560     // packages are stored in a single file, rather than a directory
561     // like normal packages, and can be downloaded by the installer
562     // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create
563     // flat packages when the packages will be downloaded on the fly.
564     std::string pkgId =
565       cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"), '.',
566                this->GetOption("CPACK_PACKAGE_NAME"), '.', component.Name);
567 
568     pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
569            << "\" --root \"" << packageDir << "\""
570            << " --id " << pkgId << " --target "
571            << this->GetOption("CPACK_OSX_PACKAGE_VERSION") << " --out \""
572            << packageFile << "\"";
573   }
574 
575   // Run PackageMaker
576   return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
577 }
578