cmCPackPackageMakerGenerator.cxx 21.3 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
4
#include "cmCPackPackageMakerGenerator.h"

5
6
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
Daniel Pfeifer's avatar
Daniel Pfeifer committed
7
8
9
10
11
12
13
#include <assert.h>
#include <map>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>

14
#include "cmCPackComponentGroup.h"
15
#include "cmCPackLog.h"
16
17
#include "cmGeneratedFileStream.h"
#include "cmSystemTools.h"
18
#include "cmXMLWriter.h"
19

20
static inline unsigned int getVersion(unsigned int major, unsigned int minor)
21
22
23
24
25
{
  assert(major < 256 && minor < 256);
  return ((major & 0xFF) << 16 | minor);
}

26
27
cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator()
{
28
  this->PackageMakerVersion = 0.0;
29
  this->PackageCompatibilityVersion = getVersion(10, 4);
30
31
32
33
34
35
}

cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
{
}

36
37
bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
{
38
  return this->PackageCompatibilityVersion >= getVersion(10, 4);
39
40
}

41
int cmCPackPackageMakerGenerator::PackageFiles()
42
{
43
44
  // TODO: Use toplevel
  //       It is used! Is this an obsolete comment?
45
46

  std::string resDir; // Where this package's resources will go.
47
48
49
  std::string packageDirFileName =
    this->GetOption("CPACK_TEMPORARY_DIRECTORY");
  if (this->Components.empty()) {
50
51
52
    packageDirFileName += ".pkg";
    resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
    resDir += "/Resources";
53
  } else {
54
    packageDirFileName += ".mpkg";
55
    if (!cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str())) {
56
      cmCPackLogger(cmCPackLog::LOG_ERROR,
57
58
59
60
                    "unable to create package directory " << packageDirFileName
                                                          << std::endl);
      return 0;
    }
61
62
63

    resDir = packageDirFileName;
    resDir += "/Contents";
64
    if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) {
65
      cmCPackLogger(cmCPackLog::LOG_ERROR,
66
                    "unable to create package subdirectory " << resDir
67
68
69
                                                             << std::endl);
      return 0;
    }
70
71

    resDir += "/Resources";
72
    if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) {
73
      cmCPackLogger(cmCPackLog::LOG_ERROR,
74
                    "unable to create package subdirectory " << resDir
75
76
77
                                                             << std::endl);
      return 0;
    }
78
79

    resDir += "/en.lproj";
80
  }
81

82
83
84
  const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
  const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
  const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
85

86
  if (this->Components.empty()) {
87
88
89
90
91
    // Create directory structure
    std::string preflightDirName = resDir + "/PreFlight";
    std::string postflightDirName = resDir + "/PostFlight";
    // if preflight or postflight scripts not there create directories
    // of the same name, I think this makes it work
92
93
    if (!preflight) {
      if (!cmsys::SystemTools::MakeDirectory(preflightDirName.c_str())) {
94
95
        cmCPackLogger(cmCPackLog::LOG_ERROR,
                      "Problem creating installer directory: "
96
                        << preflightDirName << std::endl);
97
98
        return 0;
      }
99
100
101
    }
    if (!postflight) {
      if (!cmsys::SystemTools::MakeDirectory(postflightDirName.c_str())) {
102
103
        cmCPackLogger(cmCPackLog::LOG_ERROR,
                      "Problem creating installer directory: "
104
                        << postflightDirName << std::endl);
105
106
        return 0;
      }
107
    }
108
109
110
    // if preflight, postflight, or postupgrade are set
    // then copy them into the resource directory and make
    // them executable
111
    if (preflight) {
112
      this->CopyInstallScript(resDir, preflight, "preflight");
113
    }
114
    if (postflight) {
115
      this->CopyInstallScript(resDir, postflight, "postflight");
116
117
    }
    if (postupgrade) {
118
      this->CopyInstallScript(resDir, postupgrade, "postupgrade");
119
120
    }
  } else if (postflight) {
121
122
123
124
125
126
127
128
    // create a postflight component to house the script
    this->PostFlightComponent.Name = "PostFlight";
    this->PostFlightComponent.DisplayName = "PostFlight";
    this->PostFlightComponent.Description = "PostFlight";
    this->PostFlightComponent.IsHidden = true;

    // empty directory for pkg contents
    std::string packageDir = toplevel + "/" + PostFlightComponent.Name;
129
    if (!cmsys::SystemTools::MakeDirectory(packageDir.c_str())) {
130
      cmCPackLogger(cmCPackLog::LOG_ERROR,
131
                    "Problem creating component packages directory: "
132
                      << packageDir << std::endl);
133
      return 0;
134
    }
135
136
137

    // create package
    std::string packageFileDir = packageDirFileName + "/Contents/Packages/";
138
139
140
141
    if (!cmsys::SystemTools::MakeDirectory(packageFileDir.c_str())) {
      cmCPackLogger(
        cmCPackLog::LOG_ERROR,
        "Problem creating component PostFlight Packages directory: "
142
          << packageFileDir << std::endl);
143
      return 0;
144
145
146
147
148
    }
    std::string packageFile =
      packageFileDir + this->GetPackageName(PostFlightComponent);
    if (!this->GenerateComponentPackage(
          packageFile.c_str(), packageDir.c_str(), PostFlightComponent)) {
149
      return 0;
150
    }
151
152
153

    // copy postflight script into resource directory of .pkg
    std::string resourceDir = packageFile + "/Contents/Resources";
154
    this->CopyInstallScript(resourceDir, postflight, "postflight");
155
  }
156

157
  if (!this->Components.empty()) {
158
    // Create the directory where component packages will be built.
159
160
    std::string basePackageDir = packageDirFileName;
    basePackageDir += "/Contents/Packages";
161
    if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) {
162
163
      cmCPackLogger(cmCPackLog::LOG_ERROR,
                    "Problem creating component packages directory: "
164
                      << basePackageDir << std::endl);
165
      return 0;
166
    }
167

168
169
    // Create the directory where downloaded component packages will
    // be placed.
170
171
    const char* userUploadDirectory =
      this->GetOption("CPACK_UPLOAD_DIRECTORY");
172
    std::string uploadDirectory;
173
    if (userUploadDirectory && *userUploadDirectory) {
174
      uploadDirectory = userUploadDirectory;
175
176
    } else {
      uploadDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY");
177
      uploadDirectory += "/CPackUploads";
178
    }
179

180
    // Create packages for each component
181
182
    bool warnedAboutDownloadCompatibility = false;

183
184
    std::map<std::string, cmCPackComponent>::iterator compIt;
    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
191
192
          // Build this package within the upload directory.
          packageFile = uploadDirectory;

193
194
          if (!cmSystemTools::FileExists(uploadDirectory.c_str())) {
            if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) {
195
              cmCPackLogger(cmCPackLog::LOG_ERROR,
196
                            "Unable to create package upload directory "
197
                              << uploadDirectory << std::endl);
198
199
200
              return 0;
            }
          }
201
202
        } else if (!warnedAboutDownloadCompatibility) {
          if (this->PackageCompatibilityVersion < getVersion(10, 5)) {
203
204
205
206
207
            cmCPackLogger(
              cmCPackLog::LOG_WARNING,
              "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 "
              "or greater enable downloaded packages. CPack will build a "
              "non-downloaded package."
208
209
                << std::endl);
          }
210

211
          if (this->PackageMakerVersion < 3) {
212
            cmCPackLogger(cmCPackLog::LOG_WARNING,
213
                          "CPack warning: unable to build downloaded "
214
215
                          "packages with PackageMaker versions prior "
                          "to 3.0. CPack will build a non-downloaded package."
216
217
                            << std::endl);
          }
218
219
220

          warnedAboutDownloadCompatibility = true;
        }
221
      }
222

223
      if (packageFile.empty()) {
224
225
226
227
228
229
230
        // Build this package within the overall distribution
        // metapackage.
        packageFile = basePackageDir;

        // We're not downloading this component, even if the user
        // requested it.
        compIt->second.IsDownloaded = false;
231
      }
232

233
234
235
236
237
238
      packageFile += '/';
      packageFile += GetPackageName(compIt->second);

      std::string packageDir = toplevel;
      packageDir += '/';
      packageDir += compIt->first;
239
240
      if (!this->GenerateComponentPackage(
            packageFile.c_str(), packageDir.c_str(), compIt->second)) {
241
242
243
        return 0;
      }
    }
244
  }
245
246
247
  this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");

  // Copy or create all of the resource files we need.
248
249
250
  if (!this->CopyCreateResourceFile("License", resDir) ||
      !this->CopyCreateResourceFile("ReadMe", resDir) ||
      !this->CopyCreateResourceFile("Welcome", resDir) ||
251
252
      !this->CopyResourcePlistFile("Info.plist") ||
      !this->CopyResourcePlistFile("Description.plist")) {
253
    cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
254
                    << std::endl);
255
    return 0;
256
  }
257

258
  if (this->Components.empty()) {
259
    // Use PackageMaker to build the package.
260
    std::ostringstream pkgCmd;
261
262
    pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
           << "\" -build -p \"" << packageDirFileName << "\"";
263
    if (this->Components.empty()) {
264
      pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
265
    } else {
266
267
      pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
             << "/packages/";
268
    }
269
270
    pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
           << "/Resources\" -i \""
271
           << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
272
           << "/Info.plist\" -d \""
273
           << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
274
           << "/Description.plist\"";
275
    if (this->PackageMakerVersion > 2.0) {
276
      pkgCmd << " -v";
277
    }
278
    if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str())) {
279
      return 0;
280
    }
281
  } else {
282
283
    // We have built the package in place. Generate the
    // distribution.dist file to describe it for the installer.
284
    WriteDistributionFile(packageDirFileName.c_str());
285
  }
286
287

  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
288
  tmpFile += "/hdiutilOutput.log";
289
  std::ostringstream dmgCmd;
290
  dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
291
292
         << "\" create -ov -fs HFS+ -format UDZO -srcfolder \""
         << packageDirFileName << "\" \"" << packageFileNames[0] << "\"";
293
294
  std::string output;
  int retVal = 1;
295
  int numTries = 10;
296
  bool res = false;
297
  while (numTries > 0) {
Matthias Männich's avatar
Matthias Männich committed
298
299
300
    res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
                                          &output, &retVal, nullptr,
                                          this->GeneratorVerbose, 0);
301
    if (res && !retVal) {
302
      numTries = -1;
303
      break;
304
    }
305
    cmSystemTools::Delay(500);
306
    numTries--;
307
308
  }
  if (!res || retVal) {
309
    cmGeneratedFileStream ofs(tmpFile.c_str());
310
    ofs << "# Run command: " << dmgCmd.str() << std::endl
311
        << "# Output:" << std::endl
312
        << output << std::endl;
313
    cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
314
315
                    << dmgCmd.str() << std::endl
                    << "Please check " << tmpFile << " for errors"
316
                    << std::endl);
317
    return 0;
318
  }
319
320
321
322

  return 1;
}

323
int cmCPackPackageMakerGenerator::InitializeInternal()
324
{
325
326
  this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr");

327
328
329
330
331
332
333
334
335
336
337
338
339
340
  // Starting with Xcode 4.3, PackageMaker is a separate app, and you
  // can put it anywhere you want. So... use a variable for its location.
  // People who put it in unexpected places can use the variable to tell
  // us where it is.
  //
  // Use the following locations, in "most recent installation" order,
  // to search for the PackageMaker app. Assume people who copy it into
  // the new Xcode 4.3 app in "/Applications" will copy it into the nested
  // Applications folder inside the Xcode bundle itself. Or directly in
  // the "/Applications" directory.
  //
  // If found, save result in the CPACK_INSTALLER_PROGRAM variable.

  std::vector<std::string> paths;
341
342
343
344
345
346
347
348
349
350
  paths.push_back("/Applications/Xcode.app/Contents/Applications"
                  "/PackageMaker.app/Contents/MacOS");
  paths.push_back("/Applications/Utilities"
                  "/PackageMaker.app/Contents/MacOS");
  paths.push_back("/Applications"
                  "/PackageMaker.app/Contents/MacOS");
  paths.push_back("/Developer/Applications/Utilities"
                  "/PackageMaker.app/Contents/MacOS");
  paths.push_back("/Developer/Applications"
                  "/PackageMaker.app/Contents/MacOS");
351
352

  std::string pkgPath;
353
354
  const char* inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
  if (inst_program && *inst_program) {
355
    pkgPath = inst_program;
356
  } else {
357
    pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false);
358
    if (pkgPath.empty()) {
359
      cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler"
360
                      << std::endl);
361
      return 0;
362
    }
363
364
    this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
  }
365

366
  // Get path to the real PackageMaker, not a symlink:
367
  pkgPath = cmSystemTools::GetRealPath(pkgPath);
368
  // Up from there to find the version.plist file in the "Contents" dir:
369
370
371
372
373
374
  std::string contents_dir;
  contents_dir = cmSystemTools::GetFilenamePath(pkgPath);
  contents_dir = cmSystemTools::GetFilenamePath(contents_dir);

  std::string versionFile = contents_dir + "/version.plist";

375
  if (!cmSystemTools::FileExists(versionFile.c_str())) {
376
    cmCPackLogger(cmCPackLog::LOG_ERROR,
377
                  "Cannot find PackageMaker compiler version file: "
378
                    << versionFile << std::endl);
379
    return 0;
380
  }
381

382
  cmsys::ifstream ifs(versionFile.c_str());
383
  if (!ifs) {
384
    cmCPackLogger(cmCPackLog::LOG_ERROR,
385
386
                  "Cannot open PackageMaker compiler version file"
                    << std::endl);
387
    return 0;
388
  }
389

390
391
  // Check the PackageMaker version
  cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>");
392
  cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>");
393
394
  std::string line;
  bool foundKey = false;
395
396
  while (cmSystemTools::GetLineFromStream(ifs, line)) {
    if (rexKey.find(line)) {
397
398
399
      foundKey = true;
      break;
    }
400
401
402
403
  }
  if (!foundKey) {
    cmCPackLogger(
      cmCPackLog::LOG_ERROR,
404
      "Cannot find CFBundleShortVersionString in the PackageMaker compiler "
405
406
      "version file"
        << std::endl);
407
    return 0;
408
409
  }
  if (!cmSystemTools::GetLineFromStream(ifs, line) || !rexVersion.find(line)) {
410
    cmCPackLogger(cmCPackLog::LOG_ERROR,
411
                  "Problem reading the PackageMaker compiler version file: "
412
                    << versionFile << std::endl);
413
    return 0;
414
  }
415
  this->PackageMakerVersion = atof(rexVersion.match(1).c_str());
416
  if (this->PackageMakerVersion < 1.0) {
417
    cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher"
418
                    << std::endl);
419
    return 0;
420
  }
421
  cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: "
422
                  << this->PackageMakerVersion << std::endl);
423

424
425
426
  // Determine the package compatibility version. If it wasn't
  // specified by the user, we define it based on which features the
  // user requested.
427
428
  const char* packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
  if (packageCompat && *packageCompat) {
429
430
431
    unsigned int majorVersion = 10;
    unsigned int minorVersion = 5;
    int res = sscanf(packageCompat, "%u.%u", &majorVersion, &minorVersion);
432
    if (res == 2) {
433
434
      this->PackageCompatibilityVersion =
        getVersion(majorVersion, minorVersion);
435
    }
436
  } else if (this->GetOption("CPACK_DOWNLOAD_SITE")) {
437
    this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5");
438
    this->PackageCompatibilityVersion = getVersion(10, 5);
439
  } else if (this->GetOption("CPACK_COMPONENTS_ALL")) {
440
    this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4");
441
    this->PackageCompatibilityVersion = getVersion(10, 4);
442
  } else {
443
    this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3");
444
    this->PackageCompatibilityVersion = getVersion(10, 3);
445
  }
446

447
448
  std::vector<std::string> no_paths;
  pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false);
449
  if (pkgPath.empty()) {
450
    cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
451
                    << std::endl);
452
    return 0;
453
  }
454
  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
Ken Martin's avatar
Ken Martin committed
455
                          pkgPath.c_str());
456

457
  return this->Superclass::InitializeInternal();
458
459
}

460
461
bool cmCPackPackageMakerGenerator::RunPackageMaker(const char* command,
                                                   const char* packageFile)
462
463
464
465
466
467
468
{
  std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
  tmpFile += "/PackageMakerOutput.log";

  cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
  std::string output;
  int retVal = 1;
469
  bool res = cmSystemTools::RunSingleCommand(
Matthias Männich's avatar
Matthias Männich committed
470
    command, &output, &output, &retVal, nullptr, this->GeneratorVerbose, 0);
471
  cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
472
473
                  << std::endl);
  if (!res || retVal) {
474
475
    cmGeneratedFileStream ofs(tmpFile.c_str());
    ofs << "# Run command: " << command << std::endl
476
        << "# Output:" << std::endl
477
        << output << std::endl;
478
479
480
    cmCPackLogger(
      cmCPackLog::LOG_ERROR, "Problem running PackageMaker command: "
        << command << std::endl
481
        << "Please check " << tmpFile << " for errors" << std::endl);
482
    return false;
483
  }
484
485
486
  // sometimes the command finishes but the directory is not yet
  // created, so try 10 times to see if it shows up
  int tries = 10;
487
  while (tries > 0 && !cmSystemTools::FileExists(packageFile)) {
488
489
    cmSystemTools::Delay(500);
    tries--;
490
491
492
493
494
495
  }
  if (!cmSystemTools::FileExists(packageFile)) {
    cmCPackLogger(cmCPackLog::LOG_ERROR,
                  "Problem running PackageMaker command: "
                    << command << std::endl
                    << "Package not created: " << packageFile << std::endl);
496
    return false;
497
  }
498
499
500
501

  return true;
}

502
503
504
bool cmCPackPackageMakerGenerator::GenerateComponentPackage(
  const char* packageFile, const char* packageDir,
  const cmCPackComponent& component)
505
{
506
507
  cmCPackLogger(cmCPackLog::LOG_OUTPUT, "-   Building component package: "
                  << packageFile << std::endl);
508

509
  // The command that will be used to run PackageMaker
510
  std::ostringstream pkgCmd;
511

512
  if (this->PackageCompatibilityVersion < getVersion(10, 5) ||
513
      this->PackageMakerVersion < 3.0) {
514
515
516
517
    // Create Description.plist and Info.plist files for normal Mac OS
    // X packages, which work on Mac OS X 10.3 and newer.
    std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
    descriptionFile += '/' + component.Name + "-Description.plist";
518
    cmsys::ofstream out(descriptionFile.c_str());
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
    cmXMLWriter xout(out);
    xout.StartDocument();
    xout.Doctype("plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
                 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
    xout.StartElement("plist");
    xout.Attribute("version", "1.4");
    xout.StartElement("dict");
    xout.Element("key", "IFPkgDescriptionTitle");
    xout.Element("string", component.DisplayName);
    xout.Element("key", "IFPkgDescriptionVersion");
    xout.Element("string", this->GetOption("CPACK_PACKAGE_VERSION"));
    xout.Element("key", "IFPkgDescriptionDescription");
    xout.Element("string", component.Description);
    xout.EndElement(); // dict
    xout.EndElement(); // plist
    xout.EndDocument();
535
536
537
538
539
    out.close();

    // Create the Info.plist file for this component
    std::string moduleVersionSuffix = ".";
    moduleVersionSuffix += component.Name;
540
    this->SetOption("CPACK_MODULE_VERSION_SUFFIX",
541
                    moduleVersionSuffix.c_str());
542
543
    std::string infoFileName = component.Name;
    infoFileName += "-Info.plist";
544
    if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str())) {
545
      return false;
546
    }
547
548
549
550

    pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
           << "\" -build -p \"" << packageFile << "\""
           << " -f \"" << packageDir << "\""
551
552
           << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/"
           << infoFileName << "\""
553
           << " -d \"" << descriptionFile << "\"";
554
  } else {
555
556
557
558
559
560
561
562
563
564
565
566
567
568
    // Create a "flat" package on Mac OS X 10.5 and newer. Flat
    // packages are stored in a single file, rather than a directory
    // like normal packages, and can be downloaded by the installer
    // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create
    // flat packages when the packages will be downloaded on the fly.
    std::string pkgId = "com.";
    pkgId += this->GetOption("CPACK_PACKAGE_VENDOR");
    pkgId += '.';
    pkgId += this->GetOption("CPACK_PACKAGE_NAME");
    pkgId += '.';
    pkgId += component.Name;

    pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
           << "\" --root \"" << packageDir << "\""
569
570
571
572
           << " --id " << pkgId << " --target "
           << this->GetOption("CPACK_OSX_PACKAGE_VERSION") << " --out \""
           << packageFile << "\"";
  }
573

574
  // Run PackageMaker
575
576
  return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
}