1 /*============================================================================
2 CMake - Cross Platform Makefile Generator
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
13 #include "cmCPackDragNDropGenerator.h"
14 #include "cmCPackLog.h"
15 #include "cmSystemTools.h"
16 #include "cmGeneratedFileStream.h"
18 #include <cmsys/RegularExpression.hxx>
20 static const char* SLAHeader =
21 "data 'LPic' (5000) {\n"
22 " $\"0002 0011 0003 0001 0000 0000 0002 0000\"\n"
23 " $\"0008 0003 0000 0001 0004 0000 0004 0005\"\n"
24 " $\"0000 000E 0006 0001 0005 0007 0000 0007\"\n"
25 " $\"0008 0000 0047 0009 0000 0034 000A 0001\"\n"
26 " $\"0035 000B 0001 0020 000C 0000 0011 000D\"\n"
27 " $\"0000 005B 0004 0000 0033 000F 0001 000C\"\n"
28 " $\"0010 0000 000B 000E 0000\"\n"
32 static const char* SLASTREnglish =
33 "resource 'STR#' (5002, \"English\") {\n"
40 " \"You agree to the License Agreement terms when you click \"\n"
41 " \"the \\\"Agree\\\" button.\",\n"
42 " \"Software License Agreement\",\n"
43 " \"This text cannot be saved. This disk may be full or locked, "
45 " \"file may be locked.\",\n"
46 " \"Unable to print. Make sure you have selected a printer.\"\n"
51 //----------------------------------------------------------------------
52 cmCPackDragNDropGenerator::cmCPackDragNDropGenerator()
54 // default to one package file for components
55 this->componentPackageMethod = ONE_PACKAGE;
58 //----------------------------------------------------------------------
59 cmCPackDragNDropGenerator::~cmCPackDragNDropGenerator()
63 //----------------------------------------------------------------------
64 int cmCPackDragNDropGenerator::InitializeInternal()
66 // Starting with Xcode 4.3, look in "/Applications/Xcode.app" first:
68 std::vector<std::string> paths;
69 paths.push_back("/Applications/Xcode.app/Contents/Developer/Tools");
70 paths.push_back("/Developer/Tools");
72 const std::string hdiutil_path = cmSystemTools::FindProgram("hdiutil",
73 std::vector<std::string>(), false);
74 if(hdiutil_path.empty())
76 cmCPackLogger(cmCPackLog::LOG_ERROR,
77 "Cannot locate hdiutil command"
81 this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str());
83 const std::string setfile_path = cmSystemTools::FindProgram("SetFile",
85 if(setfile_path.empty())
87 cmCPackLogger(cmCPackLog::LOG_ERROR,
88 "Cannot locate SetFile command"
92 this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str());
94 const std::string rez_path = cmSystemTools::FindProgram("Rez",
98 cmCPackLogger(cmCPackLog::LOG_ERROR,
99 "Cannot locate Rez command"
103 this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path.c_str());
105 return this->Superclass::InitializeInternal();
108 //----------------------------------------------------------------------
109 const char* cmCPackDragNDropGenerator::GetOutputExtension()
114 //----------------------------------------------------------------------
115 int cmCPackDragNDropGenerator::PackageFiles()
117 // gather which directories to make dmg files for
118 // multiple directories occur if packaging components or groups separately
121 if(this->Components.empty())
123 return this->CreateDMG(toplevel, packageFileNames[0]);
127 std::vector<std::string> package_files;
129 std::map<std::string, cmCPackComponent>::iterator compIt;
130 for (compIt=this->Components.begin();
131 compIt!=this->Components.end(); ++compIt )
133 std::string name = GetComponentInstallDirNameSuffix(compIt->first);
134 package_files.push_back(name);
136 std::sort(package_files.begin(), package_files.end());
137 package_files.erase(std::unique(package_files.begin(),
138 package_files.end()),
139 package_files.end());
142 // loop to create dmg files
143 packageFileNames.clear();
144 for(size_t i=0; i<package_files.size(); i++)
146 std::string full_package_name = std::string(toplevel) + std::string("/");
147 if(package_files[i] == "ALL_IN_ONE")
149 full_package_name += this->GetOption("CPACK_PACKAGE_FILE_NAME");
153 full_package_name += package_files[i];
155 full_package_name += std::string(GetOutputExtension());
156 packageFileNames.push_back(full_package_name);
158 std::string src_dir = toplevel;
160 src_dir += package_files[i];
162 if(0 == this->CreateDMG(src_dir, full_package_name))
170 //----------------------------------------------------------------------
171 bool cmCPackDragNDropGenerator::CopyFile(cmOStringStream& source,
172 cmOStringStream& target)
174 if(!cmSystemTools::CopyFileIfDifferent(
175 source.str().c_str(),
176 target.str().c_str()))
178 cmCPackLogger(cmCPackLog::LOG_ERROR,
191 //----------------------------------------------------------------------
192 bool cmCPackDragNDropGenerator::RunCommand(cmOStringStream& command,
197 bool result = cmSystemTools::RunSingleCommand(
198 command.str().c_str(),
202 this->GeneratorVerbose,
205 if(!result || exit_code)
207 cmCPackLogger(cmCPackLog::LOG_ERROR,
218 //----------------------------------------------------------------------
219 int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
220 const std::string& output_file)
222 // Get optional arguments ...
223 const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON")
224 ? this->GetOption("CPACK_PACKAGE_ICON") : "";
226 const std::string cpack_dmg_volume_name =
227 this->GetOption("CPACK_DMG_VOLUME_NAME")
228 ? this->GetOption("CPACK_DMG_VOLUME_NAME")
229 : this->GetOption("CPACK_PACKAGE_FILE_NAME");
231 const std::string cpack_dmg_format =
232 this->GetOption("CPACK_DMG_FORMAT")
233 ? this->GetOption("CPACK_DMG_FORMAT") : "UDZO";
235 // Get optional arguments ...
236 std::string cpack_license_file =
237 this->GetOption("CPACK_RESOURCE_FILE_LICENSE") ?
238 this->GetOption("CPACK_RESOURCE_FILE_LICENSE") : "";
240 const std::string cpack_dmg_background_image =
241 this->GetOption("CPACK_DMG_BACKGROUND_IMAGE")
242 ? this->GetOption("CPACK_DMG_BACKGROUND_IMAGE") : "";
244 const std::string cpack_dmg_ds_store =
245 this->GetOption("CPACK_DMG_DS_STORE")
246 ? this->GetOption("CPACK_DMG_DS_STORE") : "";
248 // only put license on dmg if is user provided
249 if(!cpack_license_file.empty() &&
250 cpack_license_file.find("CPack.GenericLicense.txt") != std::string::npos)
252 cpack_license_file = "";
255 // The staging directory contains everything that will end-up inside the
256 // final disk image ...
257 cmOStringStream staging;
260 // Add a symlink to /Applications so users can drag-and-drop the bundle
262 cmOStringStream application_link;
263 application_link << staging.str() << "/Applications";
264 cmSystemTools::CreateSymlink("/Applications",
265 application_link.str().c_str());
267 // Optionally add a custom volume icon ...
268 if(!cpack_package_icon.empty())
270 cmOStringStream package_icon_source;
271 package_icon_source << cpack_package_icon;
273 cmOStringStream package_icon_destination;
274 package_icon_destination << staging.str() << "/.VolumeIcon.icns";
276 if(!this->CopyFile(package_icon_source, package_icon_destination))
278 cmCPackLogger(cmCPackLog::LOG_ERROR,
279 "Error copying disk volume icon. "
280 "Check the value of CPACK_PACKAGE_ICON."
287 // Optionally add a custom .DS_Store file
288 // (e.g. for setting background/layout) ...
289 if(!cpack_dmg_ds_store.empty())
291 cmOStringStream package_settings_source;
292 package_settings_source << cpack_dmg_ds_store;
294 cmOStringStream package_settings_destination;
295 package_settings_destination << staging.str() << "/.DS_Store";
297 if(!this->CopyFile(package_settings_source, package_settings_destination))
299 cmCPackLogger(cmCPackLog::LOG_ERROR,
300 "Error copying disk volume settings file. "
301 "Check the value of CPACK_DMG_DS_STORE."
308 // Optionally add a custom background image ...
309 if(!cpack_dmg_background_image.empty())
311 cmOStringStream package_background_source;
312 package_background_source << cpack_dmg_background_image;
314 cmOStringStream package_background_destination;
315 package_background_destination << staging.str() << "/background.png";
317 if(!this->CopyFile(package_background_source,
318 package_background_destination))
320 cmCPackLogger(cmCPackLog::LOG_ERROR,
321 "Error copying disk volume background image. "
322 "Check the value of CPACK_DMG_BACKGROUND_IMAGE."
328 cmOStringStream temp_background_hiding_command;
329 temp_background_hiding_command << this->GetOption("CPACK_COMMAND_SETFILE");
330 temp_background_hiding_command << " -a V \"";
331 temp_background_hiding_command << package_background_destination.str();
332 temp_background_hiding_command << "\"";
334 if(!this->RunCommand(temp_background_hiding_command))
336 cmCPackLogger(cmCPackLog::LOG_ERROR,
337 "Error setting attributes on disk volume background image."
344 // Create a temporary read-write disk image ...
345 std::string temp_image = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
346 temp_image += "/temp.dmg";
348 cmOStringStream temp_image_command;
349 temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
350 temp_image_command << " create";
351 temp_image_command << " -ov";
352 temp_image_command << " -srcfolder \"" << staging.str() << "\"";
353 temp_image_command << " -volname \""
354 << cpack_dmg_volume_name << "\"";
355 temp_image_command << " -format UDRW";
356 temp_image_command << " \"" << temp_image << "\"";
358 if(!this->RunCommand(temp_image_command))
360 cmCPackLogger(cmCPackLog::LOG_ERROR,
361 "Error generating temporary disk image."
367 // Optionally set the custom icon flag for the image ...
368 if(!cpack_package_icon.empty())
370 cmOStringStream temp_mount;
372 cmOStringStream attach_command;
373 attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
374 attach_command << " attach";
375 attach_command << " \"" << temp_image << "\"";
377 std::string attach_output;
378 if(!this->RunCommand(attach_command, &attach_output))
380 cmCPackLogger(cmCPackLog::LOG_ERROR,
381 "Error attaching temporary disk image."
387 cmsys::RegularExpression mountpoint_regex(".*(/Volumes/[^\n]+)\n.*");
388 mountpoint_regex.find(attach_output.c_str());
389 temp_mount << mountpoint_regex.match(1);
391 cmOStringStream setfile_command;
392 setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
393 setfile_command << " -a C";
394 setfile_command << " \"" << temp_mount.str() << "\"";
396 if(!this->RunCommand(setfile_command))
398 cmCPackLogger(cmCPackLog::LOG_ERROR,
399 "Error assigning custom icon to temporary disk image."
405 cmOStringStream detach_command;
406 detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
407 detach_command << " detach";
408 detach_command << " \"" << temp_mount.str() << "\"";
410 if(!this->RunCommand(detach_command))
412 cmCPackLogger(cmCPackLog::LOG_ERROR,
413 "Error detaching temporary disk image."
420 if(!cpack_license_file.empty())
422 std::string sla_r = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
426 ifs.open(cpack_license_file.c_str());
429 cmGeneratedFileStream osf(sla_r.c_str());
430 osf << "#include <CoreServices/CoreServices.r>\n\n";
433 osf << "data 'TEXT' (5002, \"English\") {\n";
437 std::getline(ifs, line);
439 std::string::size_type pos = line.find('\"');
440 while(pos != std::string::npos)
442 line.replace(pos, 1, "\\\"");
443 pos = line.find('\"', pos+2);
445 // break up long lines to avoid Rez errors
446 std::vector<std::string> lines;
447 const size_t max_line_length = 512;
448 for(size_t i=0; i<line.size(); i+= max_line_length)
450 int line_length = max_line_length;
451 if(i+max_line_length > line.size())
452 line_length = line.size()-i;
453 lines.push_back(line.substr(i, line_length));
456 for(size_t i=0; i<lines.size(); i++)
458 osf << " \"" << lines[i] << "\"\n";
464 osf << SLASTREnglish;
470 std::string temp_udco = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
471 temp_udco += "/temp-udco.dmg";
473 cmOStringStream udco_image_command;
474 udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
475 udco_image_command << " convert \"" << temp_image << "\"";
476 udco_image_command << " -format UDCO";
477 udco_image_command << " -o \"" << temp_udco << "\"";
480 if(!this->RunCommand(udco_image_command, &error))
482 cmCPackLogger(cmCPackLog::LOG_ERROR,
483 "Error converting to UDCO dmg for adding SLA." << std::endl
490 cmOStringStream unflatten_command;
491 unflatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
492 unflatten_command << " unflatten ";
493 unflatten_command << "\"" << temp_udco << "\"";
495 if(!this->RunCommand(unflatten_command, &error))
497 cmCPackLogger(cmCPackLog::LOG_ERROR,
498 "Error unflattening dmg for adding SLA." << std::endl
505 cmOStringStream embed_sla_command;
506 embed_sla_command << this->GetOption("CPACK_COMMAND_REZ");
507 embed_sla_command << " \"" << sla_r << "\"";
508 embed_sla_command << " -a -o ";
509 embed_sla_command << "\"" << temp_udco << "\"";
511 if(!this->RunCommand(embed_sla_command, &error))
513 cmCPackLogger(cmCPackLog::LOG_ERROR,
514 "Error adding SLA." << std::endl
521 cmOStringStream flatten_command;
522 flatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
523 flatten_command << " flatten ";
524 flatten_command << "\"" << temp_udco << "\"";
526 if(!this->RunCommand(flatten_command, &error))
528 cmCPackLogger(cmCPackLog::LOG_ERROR,
529 "Error flattening dmg for adding SLA." << std::endl
535 temp_image = temp_udco;
539 // Create the final compressed read-only disk image ...
540 cmOStringStream final_image_command;
541 final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
542 final_image_command << " convert \"" << temp_image << "\"";
543 final_image_command << " -format ";
544 final_image_command << cpack_dmg_format;
545 final_image_command << " -imagekey";
546 final_image_command << " zlib-level=9";
547 final_image_command << " -o \"" << output_file << "\"";
549 if(!this->RunCommand(final_image_command))
551 cmCPackLogger(cmCPackLog::LOG_ERROR,
552 "Error compressing disk image."
561 bool cmCPackDragNDropGenerator::SupportsComponentInstallation() const
567 cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix(
568 const std::string& componentName)
570 // we want to group components together that go in the same dmg package
571 std::string package_file_name = this->GetOption("CPACK_PACKAGE_FILE_NAME");
573 // we have 3 mutually exclusive modes to work in
574 // 1. all components in one package
575 // 2. each group goes in its own package with left over
576 // components in their own package
577 // 3. ignore groups - if grouping is defined, it is ignored
578 // and each component goes in its own package
580 if(this->componentPackageMethod == ONE_PACKAGE)
585 if(this->componentPackageMethod == ONE_PACKAGE_PER_GROUP)
587 // We have to find the name of the COMPONENT GROUP
588 // the current COMPONENT belongs to.
589 std::string groupVar = "CPACK_COMPONENT_" +
590 cmSystemTools::UpperCase(componentName) + "_GROUP";
591 const char* _groupName = GetOption(groupVar.c_str());
594 std::string groupName = _groupName;
596 groupName = GetComponentPackageFileName(package_file_name,
602 return GetComponentPackageFileName(package_file_name, componentName, false);