/*========================================================================= Program: CMake - Cross-Platform Makefile Generator Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cmCPackBundleGenerator.h" #include "cmCPackLog.h" #include "cmSystemTools.h" //---------------------------------------------------------------------- cmCPackBundleGenerator::cmCPackBundleGenerator() { } //---------------------------------------------------------------------- cmCPackBundleGenerator::~cmCPackBundleGenerator() { } //---------------------------------------------------------------------- int cmCPackBundleGenerator::InitializeInternal() { const std::string hdiutil_path = cmSystemTools::FindProgram("hdiutil", std::vector(), false); if(hdiutil_path.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot locate hdiutil command" << std::endl); return 0; } this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str()); const std::string setfile_path = cmSystemTools::FindProgram("SetFile", std::vector(1, "/Developer/Tools"), false); if(setfile_path.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot locate SetFile command" << std::endl); return 0; } this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str()); return this->Superclass::InitializeInternal(); } //---------------------------------------------------------------------- const char* cmCPackBundleGenerator::GetOutputExtension() { return ".dmg"; } //---------------------------------------------------------------------- const char* cmCPackBundleGenerator::GetPackagingInstallPrefix() { this->InstallPrefix = "/"; this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME"); this->InstallPrefix += ".app/Contents/Resources"; return this->InstallPrefix.c_str(); } //---------------------------------------------------------------------- int cmCPackBundleGenerator::CompressFiles(const char* outFileName, const char* toplevel, const std::vector& files) { (void) files; // Get required arguments ... const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME") ? this->GetOption("CPACK_BUNDLE_NAME") : ""; if(cpack_bundle_name.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_NAME must be set." << std::endl); return 0; } const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST") ? this->GetOption("CPACK_BUNDLE_PLIST") : ""; if(cpack_bundle_plist.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_PLIST must be set." << std::endl); return 0; } const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON") ? this->GetOption("CPACK_BUNDLE_ICON") : ""; if(cpack_bundle_icon.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_ICON must be set." << std::endl); return 0; } const std::string cpack_bundle_startup_command = this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") : ""; if(cpack_bundle_startup_command.empty()) { cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_STARTUP_COMMAND must be set." << std::endl); return 0; } // Get optional arguments ... const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON") ? this->GetOption("CPACK_PACKAGE_ICON") : ""; // The staging directory contains everything that will end-up inside the // final disk image ... cmOStringStream staging; staging << toplevel; cmOStringStream contents; contents << staging.str() << "/" << cpack_bundle_name << ".app/" << "Contents"; cmOStringStream application; application << contents.str() << "/" << "MacOS"; cmOStringStream resources; resources << contents.str() << "/" << "Resources"; // Install a required, user-provided bundle metadata file ... cmOStringStream plist_source; plist_source << cpack_bundle_plist; cmOStringStream plist_target; plist_target << contents.str() << "/" << "Info.plist"; if(!this->CopyFile(plist_source, plist_target)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error copying plist. Check the value of CPACK_BUNDLE_PLIST." << std::endl); return 0; } // Install a user-provided bundle icon ... cmOStringStream icon_source; icon_source << cpack_bundle_icon; cmOStringStream icon_target; icon_target << resources.str() << "/" << cpack_bundle_name << ".icns"; if(!this->CopyFile(icon_source, icon_target)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error copying bundle icon. Check the value of CPACK_BUNDLE_ICON." << std::endl); return 0; } // Install a user-provided startup command (could be an executable or a // script) ... cmOStringStream command_source; command_source << cpack_bundle_startup_command; cmOStringStream command_target; command_target << application.str() << "/" << cpack_bundle_name; if(!this->CopyFile(command_source, command_target)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error copying startup command. " " Check the value of CPACK_BUNDLE_STARTUP_COMMAND." << std::endl); return 0; } cmSystemTools::SetPermissions(command_target.str().c_str(), 0777); // Add a symlink to /Applications so users can drag-and-drop the bundle // into it cmOStringStream application_link; application_link << staging.str() << "/Applications"; cmSystemTools::CreateSymlink("/Applications", application_link.str().c_str()); // Optionally add a custom volume icon ... if(!cpack_package_icon.empty()) { cmOStringStream package_icon_source; package_icon_source << cpack_package_icon; cmOStringStream package_icon_destination; package_icon_destination << staging.str() << "/.VolumeIcon.icns"; if(!this->CopyFile(package_icon_source, package_icon_destination)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error copying disk volume icon. " "Check the value of CPACK_PACKAGE_ICON." << std::endl); return 0; } } // Create a temporary read-write disk image ... cmOStringStream temp_image; temp_image << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/temp.dmg"; cmOStringStream temp_image_command; temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); temp_image_command << " create"; temp_image_command << " -ov"; temp_image_command << " -srcfolder \"" << staging.str() << "\""; temp_image_command << " -volname \"" << this->GetOption("CPACK_PACKAGE_FILE_NAME") << "\""; temp_image_command << " -format UDRW"; temp_image_command << " \"" << temp_image.str() << "\""; if(!this->RunCommand(temp_image_command)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error generating temporary disk image." << std::endl); return 0; } // Optionally set the custom icon flag for the image ... if(!cpack_package_icon.empty()) { cmOStringStream temp_mount; temp_mount << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/mnt"; cmSystemTools::MakeDirectory(temp_mount.str().c_str()); cmOStringStream attach_command; attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); attach_command << " attach"; attach_command << " -mountpoint \"" << temp_mount.str() << "\""; attach_command << " \"" << temp_image.str() << "\""; if(!this->RunCommand(attach_command)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error attaching temporary disk image." << std::endl); return 0; } cmOStringStream setfile_command; setfile_command << this->GetOption("CPACK_COMMAND_SETFILE"); setfile_command << " -a C"; setfile_command << " \"" << temp_mount.str() << "\""; if(!this->RunCommand(setfile_command)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error assigning custom icon to temporary disk image." << std::endl); return 0; } cmOStringStream detach_command; detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); detach_command << " detach"; detach_command << " \"" << temp_mount.str() << "\""; if(!this->RunCommand(detach_command)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error detaching temporary disk image." << std::endl); return 0; } } // Create the final compressed read-only disk image ... cmOStringStream final_image_command; final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); final_image_command << " convert \"" << temp_image.str() << "\""; final_image_command << " -format UDZO"; final_image_command << " -imagekey"; final_image_command << " zlib-level=9"; final_image_command << " -o \"" << outFileName << "\""; if(!this->RunCommand(final_image_command)) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error compressing disk image." << std::endl); return 0; } return 1; } //---------------------------------------------------------------------- bool cmCPackBundleGenerator::CopyFile(cmOStringStream& source, cmOStringStream& target) { if(!cmSystemTools::CopyFileIfDifferent( source.str().c_str(), target.str().c_str())) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error copying " << source.str() << " to " << target.str() << std::endl); return false; } return true; } //---------------------------------------------------------------------- bool cmCPackBundleGenerator::RunCommand(cmOStringStream& command) { std::string output; int exit_code = 1; bool result = cmSystemTools::RunSingleCommand( command.str().c_str(), &output, &exit_code, 0, this->GeneratorVerbose, 0); if(!result || exit_code) { cmCPackLogger(cmCPackLog::LOG_ERROR, "Error executing: " << command.str() << std::endl); return false; } return true; }