363 lines
10 KiB
C++
363 lines
10 KiB
C++
/*=========================================================================
|
|
|
|
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<std::string>(), 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<std::string>(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<std::string>& 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;
|
|
}
|
|
|
|
// Get optional arguments ...
|
|
const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON")
|
|
? this->GetOption("CPACK_PACKAGE_ICON") : "";
|
|
|
|
const std::string cpack_bundle_startup_command =
|
|
this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND")
|
|
? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") : "";
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Optionally a user-provided startup command (could be an
|
|
// executable or a script) ...
|
|
if(!cpack_bundle_startup_command.empty())
|
|
{
|
|
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;
|
|
}
|