CMake/Source/CPack/cmCPackOSXX11Generator.cxx
David Cole 05a76d53c0 CPack: Fix retry logic when calls to hdiutil fail
The long-standing sporadic failures of CPack tests on the Mac dashboards
are caused by an occasional problem running hdiutil. To compensate for
this, a retry loop was added in the code in a previous commit: a9fa71a4
... but the logic for breaking out of the retry loop was flawed, breaking
out of the loop (and not retrying) when the hdiutil command returns an
error instead of when it returns success.

This commit fixes the flawed logic, bumps up the number of retries from
4 to 10, and adds a half-second delay in between retries.

The delay is specifically added in case a virus checker or spotlight indexer
is temporarily causing the hdiutil failure by hanging onto a newly created
file longer than hdiutil expects it to.

As with all sporadically occurring issues, we'll never know if this is
really fixed all the way. But I'll be happy even if we can only get it to
happen just a bit less often.
2012-03-09 11:39:01 -05:00

312 lines
11 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmCPackOSXX11Generator.h"
#include "cmake.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmSystemTools.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmCPackLog.h"
#include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx>
#include <sys/stat.h>
//----------------------------------------------------------------------
cmCPackOSXX11Generator::cmCPackOSXX11Generator()
{
}
//----------------------------------------------------------------------
cmCPackOSXX11Generator::~cmCPackOSXX11Generator()
{
}
//----------------------------------------------------------------------
int cmCPackOSXX11Generator::PackageFiles()
{
// TODO: Use toplevel ?
// It is used! Is this an obsolete comment?
const char* cpackPackageExecutables
= this->GetOption("CPACK_PACKAGE_EXECUTABLES");
if ( cpackPackageExecutables )
{
cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: "
<< cpackPackageExecutables << "." << std::endl);
cmOStringStream str;
cmOStringStream deleteStr;
std::vector<std::string> cpackPackageExecutablesVector;
cmSystemTools::ExpandListArgument(cpackPackageExecutables,
cpackPackageExecutablesVector);
if ( cpackPackageExecutablesVector.size() % 2 != 0 )
{
cmCPackLogger(cmCPackLog::LOG_ERROR,
"CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and "
"<icon name>." << std::endl);
return 0;
}
std::vector<std::string>::iterator it;
for ( it = cpackPackageExecutablesVector.begin();
it != cpackPackageExecutablesVector.end();
++it )
{
std::string cpackExecutableName = *it;
++ it;
this->SetOptionIfNotSet("CPACK_EXECUTABLE_NAME",
cpackExecutableName.c_str());
}
}
// Disk image directories
std::string diskImageDirectory = toplevel;
std::string diskImageBackgroundImageDir
= diskImageDirectory + "/.background";
// App bundle directories
std::string packageDirFileName = toplevel;
packageDirFileName += "/";
packageDirFileName += this->GetOption("CPACK_PACKAGE_FILE_NAME");
packageDirFileName += ".app";
std::string contentsDirectory = packageDirFileName + "/Contents";
std::string resourcesDirectory = contentsDirectory + "/Resources";
std::string appDirectory = contentsDirectory + "/MacOS";
std::string scriptDirectory = resourcesDirectory + "/Scripts";
std::string resourceFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME");
resourceFileName += ".rsrc";
const char* dir = resourcesDirectory.c_str();
const char* appdir = appDirectory.c_str();
const char* scrDir = scriptDirectory.c_str();
const char* contDir = contentsDirectory.c_str();
const char* rsrcFile = resourceFileName.c_str();
const char* iconFile = this->GetOption("CPACK_PACKAGE_ICON");
if ( iconFile )
{
std::string iconFileName = cmsys::SystemTools::GetFilenameName(
iconFile);
if ( !cmSystemTools::FileExists(iconFile) )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find icon file: "
<< iconFile << ". Please check CPACK_PACKAGE_ICON setting."
<< std::endl);
return 0;
}
std::string destFileName = resourcesDirectory + "/" + iconFileName;
this->ConfigureFile(iconFile, destFileName.c_str(), true);
this->SetOptionIfNotSet("CPACK_APPLE_GUI_ICON", iconFileName.c_str());
}
std::string applicationsLinkName = diskImageDirectory + "/Applications";
cmSystemTools::CreateSymlink("/Applications", applicationsLinkName.c_str());
if (
!this->CopyResourcePlistFile("VolumeIcon.icns",
diskImageDirectory.c_str(),
".VolumeIcon.icns", true ) ||
!this->CopyResourcePlistFile("DS_Store", diskImageDirectory.c_str(),
".DS_Store", true ) ||
!this->CopyResourcePlistFile("background.png",
diskImageBackgroundImageDir.c_str(), "background.png", true ) ||
!this->CopyResourcePlistFile("RuntimeScript", dir) ||
!this->CopyResourcePlistFile("OSXX11.Info.plist", contDir,
"Info.plist" ) ||
!this->CopyResourcePlistFile("OSXX11.main.scpt", scrDir,
"main.scpt", true ) ||
!this->CopyResourcePlistFile("OSXScriptLauncher.rsrc", dir,
rsrcFile, true) ||
!this->CopyResourcePlistFile("OSXScriptLauncher", appdir,
this->GetOption("CPACK_PACKAGE_FILE_NAME"), true)
)
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files"
<< std::endl);
return 0;
}
// Two of the files need to have execute permission, so ensure they do:
std::string runTimeScript = dir;
runTimeScript += "/";
runTimeScript += "RuntimeScript";
std::string appScriptName = appdir;
appScriptName += "/";
appScriptName += this->GetOption("CPACK_PACKAGE_FILE_NAME");
mode_t mode;
if (cmsys::SystemTools::GetPermissions(runTimeScript.c_str(), mode))
{
mode |= (S_IXUSR | S_IXGRP | S_IXOTH);
cmsys::SystemTools::SetPermissions(runTimeScript.c_str(), mode);
cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Setting: " << runTimeScript
<< " to permission: " << mode << std::endl);
}
if (cmsys::SystemTools::GetPermissions(appScriptName.c_str(), mode))
{
mode |= (S_IXUSR | S_IXGRP | S_IXOTH);
cmsys::SystemTools::SetPermissions(appScriptName.c_str(), mode);
cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Setting: " << appScriptName
<< " to permission: " << mode << std::endl);
}
std::string output;
std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/hdiutilOutput.log";
cmOStringStream dmgCmd;
dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
<< "\" create -ov -format UDZO -srcfolder \""
<< diskImageDirectory.c_str()
<< "\" \"" << packageFileNames[0] << "\"";
cmCPackLogger(cmCPackLog::LOG_VERBOSE,
"Compress disk image using command: "
<< dmgCmd.str().c_str() << std::endl);
// since we get random dashboard failures with this one
// try running it more than once
int retVal = 1;
int numTries = 10;
bool res = false;
while(numTries > 0)
{
res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
&retVal, 0,
this->GeneratorVerbose, 0);
if ( res && !retVal )
{
numTries = -1;
break;
}
cmSystemTools::Delay(500);
numTries--;
}
if ( !res || retVal )
{
cmGeneratedFileStream ofs(tmpFile.c_str());
ofs << "# Run command: " << dmgCmd.str().c_str() << std::endl
<< "# Output:" << std::endl
<< output.c_str() << std::endl;
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: "
<< dmgCmd.str().c_str() << std::endl
<< "Please check " << tmpFile.c_str() << " for errors" << std::endl);
return 0;
}
return 1;
}
//----------------------------------------------------------------------
int cmCPackOSXX11Generator::InitializeInternal()
{
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"cmCPackOSXX11Generator::Initialize()" << std::endl);
std::vector<std::string> path;
std::string pkgPath = cmSystemTools::FindProgram("hdiutil", path, false);
if ( pkgPath.empty() )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler"
<< std::endl);
return 0;
}
this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
pkgPath.c_str());
return this->Superclass::InitializeInternal();
}
//----------------------------------------------------------------------
/*
bool cmCPackOSXX11Generator::CopyCreateResourceFile(const char* name)
{
std::string uname = cmSystemTools::UpperCase(name);
std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
const char* inFileName = this->GetOption(cpackVar.c_str());
if ( !inFileName )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str()
<< " not specified. It should point to "
<< (name ? name : "(NULL)")
<< ".rtf, " << name
<< ".html, or " << name << ".txt file" << std::endl);
return false;
}
if ( !cmSystemTools::FileExists(inFileName) )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find "
<< (name ? name : "(NULL)")
<< " resource file: " << inFileName << std::endl);
return false;
}
std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: "
<< ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed."
<< std::endl);
return false;
}
std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
destFileName += "/Resources/";
destFileName += name + ext;
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
<< (inFileName ? inFileName : "(NULL)")
<< " to " << destFileName.c_str() << std::endl);
this->ConfigureFile(inFileName, destFileName.c_str());
return true;
}
*/
//----------------------------------------------------------------------
bool cmCPackOSXX11Generator::CopyResourcePlistFile(const char* name,
const char* dir, const char* outputFileName /* = 0 */,
bool copyOnly /* = false */)
{
std::string inFName = "CPack.";
inFName += name;
inFName += ".in";
std::string inFileName = this->FindTemplate(inFName.c_str());
if ( inFileName.empty() )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
<< inFName << std::endl);
return false;
}
if ( !outputFileName )
{
outputFileName = name;
}
std::string destFileName = dir;
destFileName += "/";
destFileName += outputFileName;
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
<< inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
this->ConfigureFile(inFileName.c_str(), destFileName.c_str(), copyOnly);
return true;
}
//----------------------------------------------------------------------
const char* cmCPackOSXX11Generator::GetPackagingInstallPrefix()
{
this->InstallPrefix = "/";
this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME");
this->InstallPrefix += ".app/Contents/Resources";
return this->InstallPrefix.c_str();
}