mirror of
https://github.com/reactos/CMake.git
synced 2024-11-25 04:29:52 +00:00
573fa3bf13
The purpose of the TargetType enumeration was overloaded for install type because install rules were once recorded as targets. Factor the install types out into their own enumeration.
3179 lines
84 KiB
C++
3179 lines
84 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 "cmFileCommand.h"
|
|
#include "cmake.h"
|
|
#include "cmHexFileConverter.h"
|
|
#include "cmInstallType.h"
|
|
#include "cmFileTimeComparison.h"
|
|
#include "cmCryptoHash.h"
|
|
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
#include "cm_curl.h"
|
|
#endif
|
|
|
|
#undef GetCurrentDirectory
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <cmsys/auto_ptr.hxx>
|
|
#include <cmsys/Directory.hxx>
|
|
#include <cmsys/Glob.hxx>
|
|
#include <cmsys/RegularExpression.hxx>
|
|
|
|
// Table of permissions flags.
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
static mode_t mode_owner_read = S_IREAD;
|
|
static mode_t mode_owner_write = S_IWRITE;
|
|
static mode_t mode_owner_execute = S_IEXEC;
|
|
static mode_t mode_group_read = 0;
|
|
static mode_t mode_group_write = 0;
|
|
static mode_t mode_group_execute = 0;
|
|
static mode_t mode_world_read = 0;
|
|
static mode_t mode_world_write = 0;
|
|
static mode_t mode_world_execute = 0;
|
|
static mode_t mode_setuid = 0;
|
|
static mode_t mode_setgid = 0;
|
|
#else
|
|
static mode_t mode_owner_read = S_IRUSR;
|
|
static mode_t mode_owner_write = S_IWUSR;
|
|
static mode_t mode_owner_execute = S_IXUSR;
|
|
static mode_t mode_group_read = S_IRGRP;
|
|
static mode_t mode_group_write = S_IWGRP;
|
|
static mode_t mode_group_execute = S_IXGRP;
|
|
static mode_t mode_world_read = S_IROTH;
|
|
static mode_t mode_world_write = S_IWOTH;
|
|
static mode_t mode_world_execute = S_IXOTH;
|
|
static mode_t mode_setuid = S_ISUID;
|
|
static mode_t mode_setgid = S_ISGID;
|
|
#endif
|
|
|
|
// cmLibraryCommand
|
|
bool cmFileCommand
|
|
::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
|
|
{
|
|
if(args.size() < 2 )
|
|
{
|
|
this->SetError("must be called with at least two arguments.");
|
|
return false;
|
|
}
|
|
std::string subCommand = args[0];
|
|
if ( subCommand == "WRITE" )
|
|
{
|
|
return this->HandleWriteCommand(args, false);
|
|
}
|
|
else if ( subCommand == "APPEND" )
|
|
{
|
|
return this->HandleWriteCommand(args, true);
|
|
}
|
|
else if ( subCommand == "DOWNLOAD" )
|
|
{
|
|
return this->HandleDownloadCommand(args);
|
|
}
|
|
else if ( subCommand == "UPLOAD" )
|
|
{
|
|
return this->HandleUploadCommand(args);
|
|
}
|
|
else if ( subCommand == "READ" )
|
|
{
|
|
return this->HandleReadCommand(args);
|
|
}
|
|
else if ( subCommand == "MD5" ||
|
|
subCommand == "SHA1" ||
|
|
subCommand == "SHA224" ||
|
|
subCommand == "SHA256" ||
|
|
subCommand == "SHA384" ||
|
|
subCommand == "SHA512" )
|
|
{
|
|
return this->HandleHashCommand(args);
|
|
}
|
|
else if ( subCommand == "STRINGS" )
|
|
{
|
|
return this->HandleStringsCommand(args);
|
|
}
|
|
else if ( subCommand == "GLOB" )
|
|
{
|
|
return this->HandleGlobCommand(args, false);
|
|
}
|
|
else if ( subCommand == "GLOB_RECURSE" )
|
|
{
|
|
return this->HandleGlobCommand(args, true);
|
|
}
|
|
else if ( subCommand == "MAKE_DIRECTORY" )
|
|
{
|
|
return this->HandleMakeDirectoryCommand(args);
|
|
}
|
|
else if ( subCommand == "RENAME" )
|
|
{
|
|
return this->HandleRename(args);
|
|
}
|
|
else if ( subCommand == "REMOVE" )
|
|
{
|
|
return this->HandleRemove(args, false);
|
|
}
|
|
else if ( subCommand == "REMOVE_RECURSE" )
|
|
{
|
|
return this->HandleRemove(args, true);
|
|
}
|
|
else if ( subCommand == "COPY" )
|
|
{
|
|
return this->HandleCopyCommand(args);
|
|
}
|
|
else if ( subCommand == "INSTALL" )
|
|
{
|
|
return this->HandleInstallCommand(args);
|
|
}
|
|
else if ( subCommand == "DIFFERENT" )
|
|
{
|
|
return this->HandleDifferentCommand(args);
|
|
}
|
|
else if ( subCommand == "RPATH_CHANGE" || subCommand == "CHRPATH" )
|
|
{
|
|
return this->HandleRPathChangeCommand(args);
|
|
}
|
|
else if ( subCommand == "RPATH_CHECK" )
|
|
{
|
|
return this->HandleRPathCheckCommand(args);
|
|
}
|
|
else if ( subCommand == "RPATH_REMOVE" )
|
|
{
|
|
return this->HandleRPathRemoveCommand(args);
|
|
}
|
|
else if ( subCommand == "RELATIVE_PATH" )
|
|
{
|
|
return this->HandleRelativePathCommand(args);
|
|
}
|
|
else if ( subCommand == "TO_CMAKE_PATH" )
|
|
{
|
|
return this->HandleCMakePathCommand(args, false);
|
|
}
|
|
else if ( subCommand == "TO_NATIVE_PATH" )
|
|
{
|
|
return this->HandleCMakePathCommand(args, true);
|
|
}
|
|
|
|
std::string e = "does not recognize sub-command "+subCommand;
|
|
this->SetError(e.c_str());
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args,
|
|
bool append)
|
|
{
|
|
std::string message;
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
|
|
i++; // Get rid of subcommand
|
|
|
|
std::string fileName = *i;
|
|
if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
|
|
{
|
|
fileName = this->Makefile->GetCurrentDirectory();
|
|
fileName += "/" + *i;
|
|
}
|
|
|
|
i++;
|
|
|
|
for(;i != args.end(); ++i)
|
|
{
|
|
message += *i;
|
|
}
|
|
if ( !this->Makefile->CanIWriteThisFile(fileName.c_str()) )
|
|
{
|
|
std::string e
|
|
= "attempted to write a file: " + fileName +
|
|
" into a source directory.";
|
|
this->SetError(e.c_str());
|
|
cmSystemTools::SetFatalErrorOccured();
|
|
return false;
|
|
}
|
|
std::string dir = cmSystemTools::GetFilenamePath(fileName);
|
|
cmSystemTools::MakeDirectory(dir.c_str());
|
|
|
|
mode_t mode = 0;
|
|
|
|
// Set permissions to writable
|
|
if ( cmSystemTools::GetPermissions(fileName.c_str(), mode) )
|
|
{
|
|
cmSystemTools::SetPermissions(fileName.c_str(),
|
|
#if defined( _MSC_VER ) || defined( __MINGW32__ )
|
|
mode | S_IWRITE
|
|
#elif defined( __BORLANDC__ )
|
|
mode | S_IWUSR
|
|
#else
|
|
mode | S_IWUSR | S_IWGRP
|
|
#endif
|
|
);
|
|
}
|
|
// If GetPermissions fails, pretend like it is ok. File open will fail if
|
|
// the file is not writable
|
|
std::ofstream file(fileName.c_str(), append?std::ios::app: std::ios::out);
|
|
if ( !file )
|
|
{
|
|
std::string error = "Internal CMake error when trying to open file: ";
|
|
error += fileName.c_str();
|
|
error += " for writing.";
|
|
this->SetError(error.c_str());
|
|
return false;
|
|
}
|
|
file << message;
|
|
file.close();
|
|
if(mode)
|
|
{
|
|
cmSystemTools::SetPermissions(fileName.c_str(), mode);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args)
|
|
{
|
|
if ( args.size() < 3 )
|
|
{
|
|
this->SetError("READ must be called with at least two additional "
|
|
"arguments");
|
|
return false;
|
|
}
|
|
|
|
cmCommandArgumentsHelper argHelper;
|
|
cmCommandArgumentGroup group;
|
|
|
|
cmCAString readArg (&argHelper, "READ");
|
|
cmCAString fileNameArg (&argHelper, 0);
|
|
cmCAString resultArg (&argHelper, 0);
|
|
|
|
cmCAString offsetArg (&argHelper, "OFFSET", &group);
|
|
cmCAString limitArg (&argHelper, "LIMIT", &group);
|
|
cmCAEnabler hexOutputArg (&argHelper, "HEX", &group);
|
|
readArg.Follows(0);
|
|
fileNameArg.Follows(&readArg);
|
|
resultArg.Follows(&fileNameArg);
|
|
group.Follows(&resultArg);
|
|
argHelper.Parse(&args, 0);
|
|
|
|
std::string fileName = fileNameArg.GetString();
|
|
if ( !cmsys::SystemTools::FileIsFullPath(fileName.c_str()) )
|
|
{
|
|
fileName = this->Makefile->GetCurrentDirectory();
|
|
fileName += "/" + fileNameArg.GetString();
|
|
}
|
|
|
|
std::string variable = resultArg.GetString();
|
|
|
|
// Open the specified file.
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
std::ifstream file(fileName.c_str(), std::ios::in |
|
|
(hexOutputArg.IsEnabled() ? std::ios::binary : std::ios::in));
|
|
#else
|
|
std::ifstream file(fileName.c_str(), std::ios::in);
|
|
#endif
|
|
|
|
if ( !file )
|
|
{
|
|
std::string error = "Internal CMake error when trying to open file: ";
|
|
error += fileName.c_str();
|
|
error += " for reading.";
|
|
this->SetError(error.c_str());
|
|
return false;
|
|
}
|
|
|
|
// is there a limit?
|
|
long sizeLimit = -1;
|
|
if (limitArg.GetString().size() > 0)
|
|
{
|
|
sizeLimit = atoi(limitArg.GetCString());
|
|
}
|
|
|
|
// is there an offset?
|
|
long offset = 0;
|
|
if (offsetArg.GetString().size() > 0)
|
|
{
|
|
offset = atoi(offsetArg.GetCString());
|
|
}
|
|
|
|
file.seekg(offset);
|
|
|
|
std::string output;
|
|
|
|
if (hexOutputArg.IsEnabled())
|
|
{
|
|
// Convert part of the file into hex code
|
|
char c;
|
|
while((sizeLimit != 0) && (file.get(c)))
|
|
{
|
|
char hex[4];
|
|
sprintf(hex, "%.2x", c&0xff);
|
|
output += hex;
|
|
if (sizeLimit > 0)
|
|
{
|
|
sizeLimit--;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
std::string line;
|
|
bool has_newline = false;
|
|
while (sizeLimit != 0 &&
|
|
cmSystemTools::GetLineFromStream(file, line, &has_newline,
|
|
sizeLimit) )
|
|
{
|
|
if (sizeLimit > 0)
|
|
{
|
|
sizeLimit = sizeLimit - static_cast<long>(line.size());
|
|
if (has_newline)
|
|
{
|
|
sizeLimit--;
|
|
}
|
|
if (sizeLimit < 0)
|
|
{
|
|
sizeLimit = 0;
|
|
}
|
|
}
|
|
output += line;
|
|
if ( has_newline )
|
|
{
|
|
output += "\n";
|
|
}
|
|
}
|
|
}
|
|
this->Makefile->AddDefinition(variable.c_str(), output.c_str());
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleHashCommand(std::vector<std::string> const& args)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
if(args.size() != 3)
|
|
{
|
|
cmOStringStream e;
|
|
e << args[0] << " requires a file name and output variable";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
cmsys::auto_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0].c_str()));
|
|
if(hash.get())
|
|
{
|
|
std::string out = hash->HashFile(args[1].c_str());
|
|
if(!out.empty())
|
|
{
|
|
this->Makefile->AddDefinition(args[2].c_str(), out.c_str());
|
|
return true;
|
|
}
|
|
cmOStringStream e;
|
|
e << args[0] << " failed to read file \"" << args[1] << "\": "
|
|
<< cmSystemTools::GetLastSystemError();
|
|
this->SetError(e.str().c_str());
|
|
}
|
|
return false;
|
|
#else
|
|
cmOStringStream e;
|
|
e << args[0] << " not available during bootstrap";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleStringsCommand(std::vector<std::string> const& args)
|
|
{
|
|
if(args.size() < 3)
|
|
{
|
|
this->SetError("STRINGS requires a file name and output variable");
|
|
return false;
|
|
}
|
|
|
|
// Get the file to read.
|
|
std::string fileName = args[1];
|
|
if(!cmsys::SystemTools::FileIsFullPath(fileName.c_str()))
|
|
{
|
|
fileName = this->Makefile->GetCurrentDirectory();
|
|
fileName += "/" + args[1];
|
|
}
|
|
|
|
// Get the variable in which to store the results.
|
|
std::string outVar = args[2];
|
|
|
|
// Parse the options.
|
|
enum { arg_none,
|
|
arg_limit_input,
|
|
arg_limit_output,
|
|
arg_limit_count,
|
|
arg_length_minimum,
|
|
arg_length_maximum,
|
|
arg__maximum,
|
|
arg_regex };
|
|
unsigned int minlen = 0;
|
|
unsigned int maxlen = 0;
|
|
int limit_input = -1;
|
|
int limit_output = -1;
|
|
unsigned int limit_count = 0;
|
|
cmsys::RegularExpression regex;
|
|
bool have_regex = false;
|
|
bool newline_consume = false;
|
|
bool hex_conversion_enabled = true;
|
|
int arg_mode = arg_none;
|
|
for(unsigned int i=3; i < args.size(); ++i)
|
|
{
|
|
if(args[i] == "LIMIT_INPUT")
|
|
{
|
|
arg_mode = arg_limit_input;
|
|
}
|
|
else if(args[i] == "LIMIT_OUTPUT")
|
|
{
|
|
arg_mode = arg_limit_output;
|
|
}
|
|
else if(args[i] == "LIMIT_COUNT")
|
|
{
|
|
arg_mode = arg_limit_count;
|
|
}
|
|
else if(args[i] == "LENGTH_MINIMUM")
|
|
{
|
|
arg_mode = arg_length_minimum;
|
|
}
|
|
else if(args[i] == "LENGTH_MAXIMUM")
|
|
{
|
|
arg_mode = arg_length_maximum;
|
|
}
|
|
else if(args[i] == "REGEX")
|
|
{
|
|
arg_mode = arg_regex;
|
|
}
|
|
else if(args[i] == "NEWLINE_CONSUME")
|
|
{
|
|
newline_consume = true;
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(args[i] == "NO_HEX_CONVERSION")
|
|
{
|
|
hex_conversion_enabled = false;
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(arg_mode == arg_limit_input)
|
|
{
|
|
if(sscanf(args[i].c_str(), "%d", &limit_input) != 1 ||
|
|
limit_input < 0)
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS option LIMIT_INPUT value \""
|
|
<< args[i] << "\" is not an unsigned integer.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(arg_mode == arg_limit_output)
|
|
{
|
|
if(sscanf(args[i].c_str(), "%d", &limit_output) != 1 ||
|
|
limit_output < 0)
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS option LIMIT_OUTPUT value \""
|
|
<< args[i] << "\" is not an unsigned integer.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(arg_mode == arg_limit_count)
|
|
{
|
|
int count;
|
|
if(sscanf(args[i].c_str(), "%d", &count) != 1 || count < 0)
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS option LIMIT_COUNT value \""
|
|
<< args[i] << "\" is not an unsigned integer.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
limit_count = count;
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(arg_mode == arg_length_minimum)
|
|
{
|
|
int len;
|
|
if(sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0)
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS option LENGTH_MINIMUM value \""
|
|
<< args[i] << "\" is not an unsigned integer.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
minlen = len;
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(arg_mode == arg_length_maximum)
|
|
{
|
|
int len;
|
|
if(sscanf(args[i].c_str(), "%d", &len) != 1 || len < 0)
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS option LENGTH_MAXIMUM value \""
|
|
<< args[i] << "\" is not an unsigned integer.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
maxlen = len;
|
|
arg_mode = arg_none;
|
|
}
|
|
else if(arg_mode == arg_regex)
|
|
{
|
|
if(!regex.compile(args[i].c_str()))
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS option REGEX value \""
|
|
<< args[i] << "\" could not be compiled.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
have_regex = true;
|
|
arg_mode = arg_none;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS given unknown argument \""
|
|
<< args[i] << "\"";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (hex_conversion_enabled)
|
|
{
|
|
// TODO: should work without temp file, but just on a memory buffer
|
|
std::string binaryFileName = this->Makefile->GetCurrentOutputDirectory();
|
|
binaryFileName += cmake::GetCMakeFilesDirectory();
|
|
binaryFileName += "/FileCommandStringsBinaryFile";
|
|
if(cmHexFileConverter::TryConvert(fileName.c_str(),binaryFileName.c_str()))
|
|
{
|
|
fileName = binaryFileName;
|
|
}
|
|
}
|
|
|
|
// Open the specified file.
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
std::ifstream fin(fileName.c_str(), std::ios::in | std::ios::binary);
|
|
#else
|
|
std::ifstream fin(fileName.c_str(), std::ios::in);
|
|
#endif
|
|
if(!fin)
|
|
{
|
|
cmOStringStream e;
|
|
e << "STRINGS file \"" << fileName << "\" cannot be read.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Parse strings out of the file.
|
|
int output_size = 0;
|
|
std::vector<std::string> strings;
|
|
std::string s;
|
|
int c;
|
|
while((!limit_count || strings.size() < limit_count) &&
|
|
(limit_input < 0 || static_cast<int>(fin.tellg()) < limit_input) &&
|
|
(c = fin.get(), fin))
|
|
{
|
|
if(c == '\n' && !newline_consume)
|
|
{
|
|
// The current line has been terminated. Check if the current
|
|
// string matches the requirements. The length may now be as
|
|
// low as zero since blank lines are allowed.
|
|
if(s.length() >= minlen &&
|
|
(!have_regex || regex.find(s.c_str())))
|
|
{
|
|
output_size += static_cast<int>(s.size()) + 1;
|
|
if(limit_output >= 0 && output_size >= limit_output)
|
|
{
|
|
s = "";
|
|
break;
|
|
}
|
|
strings.push_back(s);
|
|
}
|
|
|
|
// Reset the string to empty.
|
|
s = "";
|
|
}
|
|
else if(c == '\r')
|
|
{
|
|
// Ignore CR character to make output always have UNIX newlines.
|
|
}
|
|
else if((c >= 0x20 && c < 0x7F) || c == '\t' ||
|
|
(c == '\n' && newline_consume))
|
|
{
|
|
// This is an ASCII character that may be part of a string.
|
|
// Cast added to avoid compiler warning. Cast is ok because
|
|
// c is guaranteed to fit in char by the above if...
|
|
s += static_cast<char>(c);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Support ENCODING option. See issue #10519.
|
|
// A non-string character has been found. Check if the current
|
|
// string matches the requirements. We require that the length
|
|
// be at least one no matter what the user specified.
|
|
if(s.length() >= minlen && s.length() >= 1 &&
|
|
(!have_regex || regex.find(s.c_str())))
|
|
{
|
|
output_size += static_cast<int>(s.size()) + 1;
|
|
if(limit_output >= 0 && output_size >= limit_output)
|
|
{
|
|
s = "";
|
|
break;
|
|
}
|
|
strings.push_back(s);
|
|
}
|
|
|
|
// Reset the string to empty.
|
|
s = "";
|
|
}
|
|
|
|
// Terminate a string if the maximum length is reached.
|
|
if(maxlen > 0 && s.size() == maxlen)
|
|
{
|
|
if(s.length() >= minlen &&
|
|
(!have_regex || regex.find(s.c_str())))
|
|
{
|
|
output_size += static_cast<int>(s.size()) + 1;
|
|
if(limit_output >= 0 && output_size >= limit_output)
|
|
{
|
|
s = "";
|
|
break;
|
|
}
|
|
strings.push_back(s);
|
|
}
|
|
s = "";
|
|
}
|
|
}
|
|
|
|
// If there is a non-empty current string we have hit the end of the
|
|
// input file or the input size limit. Check if the current string
|
|
// matches the requirements.
|
|
if((!limit_count || strings.size() < limit_count) &&
|
|
!s.empty() && s.length() >= minlen &&
|
|
(!have_regex || regex.find(s.c_str())))
|
|
{
|
|
output_size += static_cast<int>(s.size()) + 1;
|
|
if(limit_output < 0 || output_size < limit_output)
|
|
{
|
|
strings.push_back(s);
|
|
}
|
|
}
|
|
|
|
// Encode the result in a CMake list.
|
|
const char* sep = "";
|
|
std::string output;
|
|
for(std::vector<std::string>::const_iterator si = strings.begin();
|
|
si != strings.end(); ++si)
|
|
{
|
|
// Separate the strings in the output to make it a list.
|
|
output += sep;
|
|
sep = ";";
|
|
|
|
// Store the string in the output, but escape semicolons to
|
|
// make sure it is a list.
|
|
std::string const& sr = *si;
|
|
for(unsigned int i=0; i < sr.size(); ++i)
|
|
{
|
|
if(sr[i] == ';')
|
|
{
|
|
output += '\\';
|
|
}
|
|
output += sr[i];
|
|
}
|
|
}
|
|
|
|
// Save the output in a makefile variable.
|
|
this->Makefile->AddDefinition(outVar.c_str(), output.c_str());
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args,
|
|
bool recurse)
|
|
{
|
|
if ( args.size() < 2 )
|
|
{
|
|
this->SetError("GLOB requires at least a variable name");
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
|
|
i++; // Get rid of subcommand
|
|
|
|
std::string variable = *i;
|
|
i++;
|
|
cmsys::Glob g;
|
|
g.SetRecurse(recurse);
|
|
|
|
bool explicitFollowSymlinks = false;
|
|
cmPolicies::PolicyStatus status =
|
|
this->Makefile->GetPolicyStatus(cmPolicies::CMP0009);
|
|
if(recurse)
|
|
{
|
|
switch(status)
|
|
{
|
|
case cmPolicies::NEW:
|
|
g.RecurseThroughSymlinksOff();
|
|
break;
|
|
case cmPolicies::OLD:
|
|
case cmPolicies::WARN:
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
g.RecurseThroughSymlinksOn();
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::string output = "";
|
|
bool first = true;
|
|
for ( ; i != args.end(); ++i )
|
|
{
|
|
if ( recurse && (*i == "FOLLOW_SYMLINKS") )
|
|
{
|
|
explicitFollowSymlinks = true;
|
|
g.RecurseThroughSymlinksOn();
|
|
++i;
|
|
if ( i == args.end() )
|
|
{
|
|
this->SetError(
|
|
"GLOB_RECURSE requires a glob expression after FOLLOW_SYMLINKS");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( *i == "RELATIVE" )
|
|
{
|
|
++i; // skip RELATIVE
|
|
if ( i == args.end() )
|
|
{
|
|
this->SetError("GLOB requires a directory after the RELATIVE tag");
|
|
return false;
|
|
}
|
|
g.SetRelative(i->c_str());
|
|
++i;
|
|
if(i == args.end())
|
|
{
|
|
this->SetError("GLOB requires a glob expression after the directory");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
|
|
{
|
|
std::string expr = this->Makefile->GetCurrentDirectory();
|
|
// Handle script mode
|
|
if ( expr.size() > 0 )
|
|
{
|
|
expr += "/" + *i;
|
|
g.FindFiles(expr);
|
|
}
|
|
else
|
|
{
|
|
g.FindFiles(*i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.FindFiles(*i);
|
|
}
|
|
|
|
std::vector<std::string>::size_type cc;
|
|
std::vector<std::string>& files = g.GetFiles();
|
|
for ( cc = 0; cc < files.size(); cc ++ )
|
|
{
|
|
if ( !first )
|
|
{
|
|
output += ";";
|
|
}
|
|
output += files[cc];
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
if(recurse && !explicitFollowSymlinks)
|
|
{
|
|
switch (status)
|
|
{
|
|
case cmPolicies::NEW:
|
|
// Correct behavior, yay!
|
|
break;
|
|
case cmPolicies::OLD:
|
|
// Probably not really the expected behavior, but the author explicitly
|
|
// asked for the old behavior... no warning.
|
|
case cmPolicies::WARN:
|
|
// Possibly unexpected old behavior *and* we actually traversed
|
|
// symlinks without being explicitly asked to: warn the author.
|
|
if(g.GetFollowedSymlinkCount() != 0)
|
|
{
|
|
this->Makefile->IssueMessage(cmake::AUTHOR_WARNING,
|
|
this->Makefile->GetPolicies()->
|
|
GetPolicyWarning(cmPolicies::CMP0009));
|
|
}
|
|
break;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
this->SetError("policy CMP0009 error");
|
|
this->Makefile->IssueMessage(cmake::FATAL_ERROR,
|
|
this->Makefile->GetPolicies()->
|
|
GetRequiredPolicyError(cmPolicies::CMP0009));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
this->Makefile->AddDefinition(variable.c_str(), output.c_str());
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleMakeDirectoryCommand(
|
|
std::vector<std::string> const& args)
|
|
{
|
|
if(args.size() < 2 )
|
|
{
|
|
this->SetError("called with incorrect number of arguments");
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
|
|
i++; // Get rid of subcommand
|
|
|
|
std::string expr;
|
|
for ( ; i != args.end(); ++i )
|
|
{
|
|
const std::string* cdir = &(*i);
|
|
if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
|
|
{
|
|
expr = this->Makefile->GetCurrentDirectory();
|
|
expr += "/" + *i;
|
|
cdir = &expr;
|
|
}
|
|
if ( !this->Makefile->CanIWriteThisFile(cdir->c_str()) )
|
|
{
|
|
std::string e = "attempted to create a directory: " + *cdir
|
|
+ " into a source directory.";
|
|
this->SetError(e.c_str());
|
|
cmSystemTools::SetFatalErrorOccured();
|
|
return false;
|
|
}
|
|
if ( !cmSystemTools::MakeDirectory(cdir->c_str()) )
|
|
{
|
|
std::string error = "problem creating directory: " + *cdir;
|
|
this->SetError(error.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
cmFileCommand::HandleDifferentCommand(std::vector<std::string> const& args)
|
|
{
|
|
/*
|
|
FILE(DIFFERENT <variable> FILES <lhs> <rhs>)
|
|
*/
|
|
|
|
// Evaluate arguments.
|
|
const char* file_lhs = 0;
|
|
const char* file_rhs = 0;
|
|
const char* var = 0;
|
|
enum Doing { DoingNone, DoingVar, DoingFileLHS, DoingFileRHS };
|
|
Doing doing = DoingVar;
|
|
for(unsigned int i=1; i < args.size(); ++i)
|
|
{
|
|
if(args[i] == "FILES")
|
|
{
|
|
doing = DoingFileLHS;
|
|
}
|
|
else if(doing == DoingVar)
|
|
{
|
|
var = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else if(doing == DoingFileLHS)
|
|
{
|
|
file_lhs = args[i].c_str();
|
|
doing = DoingFileRHS;
|
|
}
|
|
else if(doing == DoingFileRHS)
|
|
{
|
|
file_rhs = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "DIFFERENT given unknown argument " << args[i];
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
if(!var)
|
|
{
|
|
this->SetError("DIFFERENT not given result variable name.");
|
|
return false;
|
|
}
|
|
if(!file_lhs || !file_rhs)
|
|
{
|
|
this->SetError("DIFFERENT not given FILES option with two file names.");
|
|
return false;
|
|
}
|
|
|
|
// Compare the files.
|
|
const char* result =
|
|
cmSystemTools::FilesDiffer(file_lhs, file_rhs)? "1" : "0";
|
|
this->Makefile->AddDefinition(var, result);
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// File installation helper class.
|
|
struct cmFileCopier
|
|
{
|
|
cmFileCopier(cmFileCommand* command, const char* name = "COPY"):
|
|
FileCommand(command),
|
|
Makefile(command->GetMakefile()),
|
|
Name(name),
|
|
Always(false),
|
|
MatchlessFiles(true),
|
|
FilePermissions(0),
|
|
DirPermissions(0),
|
|
CurrentMatchRule(0),
|
|
UseGivenPermissionsFile(false),
|
|
UseGivenPermissionsDir(false),
|
|
UseSourcePermissions(true),
|
|
Doing(DoingNone)
|
|
{
|
|
}
|
|
virtual ~cmFileCopier() {}
|
|
|
|
bool Run(std::vector<std::string> const& args);
|
|
protected:
|
|
|
|
cmFileCommand* FileCommand;
|
|
cmMakefile* Makefile;
|
|
const char* Name;
|
|
bool Always;
|
|
cmFileTimeComparison FileTimes;
|
|
|
|
// Whether to install a file not matching any expression.
|
|
bool MatchlessFiles;
|
|
|
|
// Permissions for files and directories installed by this object.
|
|
mode_t FilePermissions;
|
|
mode_t DirPermissions;
|
|
|
|
// Properties set by pattern and regex match rules.
|
|
struct MatchProperties
|
|
{
|
|
bool Exclude;
|
|
mode_t Permissions;
|
|
MatchProperties(): Exclude(false), Permissions(0) {}
|
|
};
|
|
struct MatchRule;
|
|
friend struct MatchRule;
|
|
struct MatchRule
|
|
{
|
|
cmsys::RegularExpression Regex;
|
|
MatchProperties Properties;
|
|
std::string RegexString;
|
|
MatchRule(std::string const& regex):
|
|
Regex(regex.c_str()), RegexString(regex) {}
|
|
};
|
|
std::vector<MatchRule> MatchRules;
|
|
|
|
// Get the properties from rules matching this input file.
|
|
MatchProperties CollectMatchProperties(const char* file)
|
|
{
|
|
// Match rules are case-insensitive on some platforms.
|
|
#if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
|
|
std::string lower = cmSystemTools::LowerCase(file);
|
|
file = lower.c_str();
|
|
#endif
|
|
|
|
// Collect properties from all matching rules.
|
|
bool matched = false;
|
|
MatchProperties result;
|
|
for(std::vector<MatchRule>::iterator mr = this->MatchRules.begin();
|
|
mr != this->MatchRules.end(); ++mr)
|
|
{
|
|
if(mr->Regex.find(file))
|
|
{
|
|
matched = true;
|
|
result.Exclude |= mr->Properties.Exclude;
|
|
result.Permissions |= mr->Properties.Permissions;
|
|
}
|
|
}
|
|
if(!matched && !this->MatchlessFiles)
|
|
{
|
|
result.Exclude = !cmSystemTools::FileIsDirectory(file);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool SetPermissions(const char* toFile, mode_t permissions)
|
|
{
|
|
if(permissions && !cmSystemTools::SetPermissions(toFile, permissions))
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot set permissions on \"" << toFile << "\"";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Translate an argument to a permissions bit.
|
|
bool CheckPermissions(std::string const& arg, mode_t& permissions)
|
|
{
|
|
if(arg == "OWNER_READ") { permissions |= mode_owner_read; }
|
|
else if(arg == "OWNER_WRITE") { permissions |= mode_owner_write; }
|
|
else if(arg == "OWNER_EXECUTE") { permissions |= mode_owner_execute; }
|
|
else if(arg == "GROUP_READ") { permissions |= mode_group_read; }
|
|
else if(arg == "GROUP_WRITE") { permissions |= mode_group_write; }
|
|
else if(arg == "GROUP_EXECUTE") { permissions |= mode_group_execute; }
|
|
else if(arg == "WORLD_READ") { permissions |= mode_world_read; }
|
|
else if(arg == "WORLD_WRITE") { permissions |= mode_world_write; }
|
|
else if(arg == "WORLD_EXECUTE") { permissions |= mode_world_execute; }
|
|
else if(arg == "SETUID") { permissions |= mode_setuid; }
|
|
else if(arg == "SETGID") { permissions |= mode_setgid; }
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " given invalid permission \"" << arg << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool InstallSymlink(const char* fromFile, const char* toFile);
|
|
bool InstallFile(const char* fromFile, const char* toFile,
|
|
MatchProperties const& match_properties);
|
|
bool InstallDirectory(const char* source, const char* destination,
|
|
MatchProperties const& match_properties);
|
|
virtual bool Install(const char* fromFile, const char* toFile);
|
|
virtual std::string const& ToName(std::string const& fromName)
|
|
{ return fromName; }
|
|
|
|
enum Type
|
|
{
|
|
TypeFile,
|
|
TypeDir,
|
|
TypeLink
|
|
};
|
|
virtual void ReportCopy(const char*, Type, bool) {}
|
|
virtual bool ReportMissing(const char* fromFile)
|
|
{
|
|
// The input file does not exist and installation is not optional.
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot find \"" << fromFile << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
MatchRule* CurrentMatchRule;
|
|
bool UseGivenPermissionsFile;
|
|
bool UseGivenPermissionsDir;
|
|
bool UseSourcePermissions;
|
|
std::string Destination;
|
|
std::vector<std::string> Files;
|
|
int Doing;
|
|
|
|
virtual bool Parse(std::vector<std::string> const& args);
|
|
enum
|
|
{
|
|
DoingNone,
|
|
DoingError,
|
|
DoingDestination,
|
|
DoingFiles,
|
|
DoingPattern,
|
|
DoingRegex,
|
|
DoingPermissionsFile,
|
|
DoingPermissionsDir,
|
|
DoingPermissionsMatch,
|
|
DoingLast1
|
|
};
|
|
virtual bool CheckKeyword(std::string const& arg);
|
|
virtual bool CheckValue(std::string const& arg);
|
|
|
|
void NotBeforeMatch(std::string const& arg)
|
|
{
|
|
cmOStringStream e;
|
|
e << "option " << arg << " may not appear before PATTERN or REGEX.";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
this->Doing = DoingError;
|
|
}
|
|
void NotAfterMatch(std::string const& arg)
|
|
{
|
|
cmOStringStream e;
|
|
e << "option " << arg << " may not appear after PATTERN or REGEX.";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
this->Doing = DoingError;
|
|
}
|
|
virtual void DefaultFilePermissions()
|
|
{
|
|
// Use read/write permissions.
|
|
this->FilePermissions = 0;
|
|
this->FilePermissions |= mode_owner_read;
|
|
this->FilePermissions |= mode_owner_write;
|
|
this->FilePermissions |= mode_group_read;
|
|
this->FilePermissions |= mode_world_read;
|
|
}
|
|
virtual void DefaultDirectoryPermissions()
|
|
{
|
|
// Use read/write/executable permissions.
|
|
this->DirPermissions = 0;
|
|
this->DirPermissions |= mode_owner_read;
|
|
this->DirPermissions |= mode_owner_write;
|
|
this->DirPermissions |= mode_owner_execute;
|
|
this->DirPermissions |= mode_group_read;
|
|
this->DirPermissions |= mode_group_execute;
|
|
this->DirPermissions |= mode_world_read;
|
|
this->DirPermissions |= mode_world_execute;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::Parse(std::vector<std::string> const& args)
|
|
{
|
|
this->Doing = DoingFiles;
|
|
for(unsigned int i=1; i < args.size(); ++i)
|
|
{
|
|
// Check this argument.
|
|
if(!this->CheckKeyword(args[i]) &&
|
|
!this->CheckValue(args[i]))
|
|
{
|
|
cmOStringStream e;
|
|
e << "called with unknown argument \"" << args[i] << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Quit if an argument is invalid.
|
|
if(this->Doing == DoingError)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Require a destination.
|
|
if(this->Destination.empty())
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " given no DESTINATION";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// If file permissions were not specified set default permissions.
|
|
if(!this->UseGivenPermissionsFile && !this->UseSourcePermissions)
|
|
{
|
|
this->DefaultFilePermissions();
|
|
}
|
|
|
|
// If directory permissions were not specified set default permissions.
|
|
if(!this->UseGivenPermissionsDir && !this->UseSourcePermissions)
|
|
{
|
|
this->DefaultDirectoryPermissions();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::CheckKeyword(std::string const& arg)
|
|
{
|
|
if(arg == "DESTINATION")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingDestination;
|
|
}
|
|
}
|
|
else if(arg == "PATTERN")
|
|
{
|
|
this->Doing = DoingPattern;
|
|
}
|
|
else if(arg == "REGEX")
|
|
{
|
|
this->Doing = DoingRegex;
|
|
}
|
|
else if(arg == "EXCLUDE")
|
|
{
|
|
// Add this property to the current match rule.
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->CurrentMatchRule->Properties.Exclude = true;
|
|
this->Doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
this->NotBeforeMatch(arg);
|
|
}
|
|
}
|
|
else if(arg == "PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->Doing = DoingPermissionsMatch;
|
|
}
|
|
else
|
|
{
|
|
this->NotBeforeMatch(arg);
|
|
}
|
|
}
|
|
else if(arg == "FILE_PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingPermissionsFile;
|
|
this->UseGivenPermissionsFile = true;
|
|
}
|
|
}
|
|
else if(arg == "DIRECTORY_PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingPermissionsDir;
|
|
this->UseGivenPermissionsDir = true;
|
|
}
|
|
}
|
|
else if(arg == "USE_SOURCE_PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingNone;
|
|
this->UseSourcePermissions = true;
|
|
}
|
|
}
|
|
else if(arg == "NO_SOURCE_PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingNone;
|
|
this->UseSourcePermissions = false;
|
|
}
|
|
}
|
|
else if(arg == "FILES_MATCHING")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingNone;
|
|
this->MatchlessFiles = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::CheckValue(std::string const& arg)
|
|
{
|
|
switch(this->Doing)
|
|
{
|
|
case DoingFiles:
|
|
if(arg.empty() || cmSystemTools::FileIsFullPath(arg.c_str()))
|
|
{
|
|
this->Files.push_back(arg);
|
|
}
|
|
else
|
|
{
|
|
std::string file = this->Makefile->GetCurrentDirectory();
|
|
file += "/" + arg;
|
|
this->Files.push_back(file);
|
|
}
|
|
break;
|
|
case DoingDestination:
|
|
if(arg.empty() || cmSystemTools::FileIsFullPath(arg.c_str()))
|
|
{
|
|
this->Destination = arg;
|
|
}
|
|
else
|
|
{
|
|
this->Destination = this->Makefile->GetCurrentOutputDirectory();
|
|
this->Destination += "/" + arg;
|
|
}
|
|
this->Doing = DoingNone;
|
|
break;
|
|
case DoingPattern:
|
|
{
|
|
// Convert the pattern to a regular expression. Require a
|
|
// leading slash and trailing end-of-string in the matched
|
|
// string to make sure the pattern matches only whole file
|
|
// names.
|
|
std::string regex = "/";
|
|
regex += cmsys::Glob::PatternToRegex(arg, false);
|
|
regex += "$";
|
|
this->MatchRules.push_back(MatchRule(regex));
|
|
this->CurrentMatchRule = &*(this->MatchRules.end()-1);
|
|
if(this->CurrentMatchRule->Regex.is_valid())
|
|
{
|
|
this->Doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "could not compile PATTERN \"" << arg << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
this->Doing = DoingError;
|
|
}
|
|
}
|
|
break;
|
|
case DoingRegex:
|
|
this->MatchRules.push_back(MatchRule(arg));
|
|
this->CurrentMatchRule = &*(this->MatchRules.end()-1);
|
|
if(this->CurrentMatchRule->Regex.is_valid())
|
|
{
|
|
this->Doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "could not compile REGEX \"" << arg << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
this->Doing = DoingError;
|
|
}
|
|
break;
|
|
case DoingPermissionsFile:
|
|
if(!this->CheckPermissions(arg, this->FilePermissions))
|
|
{
|
|
this->Doing = DoingError;
|
|
}
|
|
break;
|
|
case DoingPermissionsDir:
|
|
if(!this->CheckPermissions(arg, this->DirPermissions))
|
|
{
|
|
this->Doing = DoingError;
|
|
}
|
|
break;
|
|
case DoingPermissionsMatch:
|
|
if(!this->CheckPermissions(
|
|
arg, this->CurrentMatchRule->Properties.Permissions))
|
|
{
|
|
this->Doing = DoingError;
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::Run(std::vector<std::string> const& args)
|
|
{
|
|
if(!this->Parse(args))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> const& files = this->Files;
|
|
for(std::vector<std::string>::size_type i = 0; i < files.size(); ++i)
|
|
{
|
|
// Split the input file into its directory and name components.
|
|
std::vector<std::string> fromPathComponents;
|
|
cmSystemTools::SplitPath(files[i].c_str(), fromPathComponents);
|
|
std::string fromName = *(fromPathComponents.end()-1);
|
|
std::string fromDir = cmSystemTools::JoinPath(fromPathComponents.begin(),
|
|
fromPathComponents.end()-1);
|
|
|
|
// Compute the full path to the destination file.
|
|
std::string toFile = this->Destination;
|
|
std::string const& toName = this->ToName(fromName);
|
|
if(!toName.empty())
|
|
{
|
|
toFile += "/";
|
|
toFile += toName;
|
|
}
|
|
|
|
// Construct the full path to the source file. The file name may
|
|
// have been changed above.
|
|
std::string fromFile = fromDir;
|
|
if(!fromName.empty())
|
|
{
|
|
fromFile += "/";
|
|
fromFile += fromName;
|
|
}
|
|
|
|
if(!this->Install(fromFile.c_str(), toFile.c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::Install(const char* fromFile, const char* toFile)
|
|
{
|
|
if(!*fromFile)
|
|
{
|
|
cmOStringStream e;
|
|
e << "INSTALL encountered an empty string input file name.";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Collect any properties matching this file name.
|
|
MatchProperties match_properties = this->CollectMatchProperties(fromFile);
|
|
|
|
// Skip the file if it is excluded.
|
|
if(match_properties.Exclude)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(cmSystemTools::SameFile(fromFile, toFile))
|
|
{
|
|
return true;
|
|
}
|
|
else if(cmSystemTools::FileIsSymlink(fromFile))
|
|
{
|
|
return this->InstallSymlink(fromFile, toFile);
|
|
}
|
|
else if(cmSystemTools::FileIsDirectory(fromFile))
|
|
{
|
|
return this->InstallDirectory(fromFile, toFile, match_properties);
|
|
}
|
|
else if(cmSystemTools::FileExists(fromFile))
|
|
{
|
|
return this->InstallFile(fromFile, toFile, match_properties);
|
|
}
|
|
return this->ReportMissing(fromFile);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::InstallSymlink(const char* fromFile, const char* toFile)
|
|
{
|
|
// Read the original symlink.
|
|
std::string symlinkTarget;
|
|
if(!cmSystemTools::ReadSymlink(fromFile, symlinkTarget))
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot read symlink \"" << fromFile
|
|
<< "\" to duplicate at \"" << toFile << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Compare the symlink value to that at the destination if not
|
|
// always installing.
|
|
bool copy = true;
|
|
if(!this->Always)
|
|
{
|
|
std::string oldSymlinkTarget;
|
|
if(cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget))
|
|
{
|
|
if(symlinkTarget == oldSymlinkTarget)
|
|
{
|
|
copy = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inform the user about this file installation.
|
|
this->ReportCopy(toFile, TypeLink, copy);
|
|
|
|
if(copy)
|
|
{
|
|
// Remove the destination file so we can always create the symlink.
|
|
cmSystemTools::RemoveFile(toFile);
|
|
|
|
// Create the symlink.
|
|
if(!cmSystemTools::CreateSymlink(symlinkTarget.c_str(), toFile))
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot duplicate symlink \"" << fromFile
|
|
<< "\" at \"" << toFile << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::InstallFile(const char* fromFile, const char* toFile,
|
|
MatchProperties const& match_properties)
|
|
{
|
|
// Determine whether we will copy the file.
|
|
bool copy = true;
|
|
if(!this->Always)
|
|
{
|
|
// If both files exist with the same time do not copy.
|
|
if(!this->FileTimes.FileTimesDiffer(fromFile, toFile))
|
|
{
|
|
copy = false;
|
|
}
|
|
}
|
|
|
|
// Inform the user about this file installation.
|
|
this->ReportCopy(toFile, TypeFile, copy);
|
|
|
|
// Copy the file.
|
|
if(copy && !cmSystemTools::CopyAFile(fromFile, toFile, true))
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot copy file \"" << fromFile
|
|
<< "\" to \"" << toFile << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Set the file modification time of the destination file.
|
|
if(copy && !this->Always)
|
|
{
|
|
// Add write permission so we can set the file time.
|
|
// Permissions are set unconditionally below anyway.
|
|
mode_t perm = 0;
|
|
if(cmSystemTools::GetPermissions(toFile, perm))
|
|
{
|
|
cmSystemTools::SetPermissions(toFile, perm | mode_owner_write);
|
|
}
|
|
if (!cmSystemTools::CopyFileTime(fromFile, toFile))
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot set modification time on \""
|
|
<< toFile << "\"";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Set permissions of the destination file.
|
|
mode_t permissions = (match_properties.Permissions?
|
|
match_properties.Permissions : this->FilePermissions);
|
|
if(!permissions)
|
|
{
|
|
// No permissions were explicitly provided but the user requested
|
|
// that the source file permissions be used.
|
|
cmSystemTools::GetPermissions(fromFile, permissions);
|
|
}
|
|
return this->SetPermissions(toFile, permissions);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCopier::InstallDirectory(const char* source,
|
|
const char* destination,
|
|
MatchProperties const& match_properties)
|
|
{
|
|
// Inform the user about this directory installation.
|
|
this->ReportCopy(destination, TypeDir, true);
|
|
|
|
// Make sure the destination directory exists.
|
|
if(!cmSystemTools::MakeDirectory(destination))
|
|
{
|
|
cmOStringStream e;
|
|
e << this->Name << " cannot make directory \"" << destination << "\": "
|
|
<< cmSystemTools::GetLastSystemError();
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
// Compute the requested permissions for the destination directory.
|
|
mode_t permissions = (match_properties.Permissions?
|
|
match_properties.Permissions : this->DirPermissions);
|
|
if(!permissions)
|
|
{
|
|
// No permissions were explicitly provided but the user requested
|
|
// that the source directory permissions be used.
|
|
cmSystemTools::GetPermissions(source, permissions);
|
|
}
|
|
|
|
// Compute the set of permissions required on this directory to
|
|
// recursively install files and subdirectories safely.
|
|
mode_t required_permissions =
|
|
mode_owner_read | mode_owner_write | mode_owner_execute;
|
|
|
|
// If the required permissions are specified it is safe to set the
|
|
// final permissions now. Otherwise we must add the required
|
|
// permissions temporarily during file installation.
|
|
mode_t permissions_before = 0;
|
|
mode_t permissions_after = 0;
|
|
if((permissions & required_permissions) == required_permissions)
|
|
{
|
|
permissions_before = permissions;
|
|
}
|
|
else
|
|
{
|
|
permissions_before = permissions | required_permissions;
|
|
permissions_after = permissions;
|
|
}
|
|
|
|
// Set the required permissions of the destination directory.
|
|
if(!this->SetPermissions(destination, permissions_before))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Load the directory contents to traverse it recursively.
|
|
cmsys::Directory dir;
|
|
if(source && *source)
|
|
{
|
|
dir.Load(source);
|
|
}
|
|
unsigned long numFiles = static_cast<unsigned long>(dir.GetNumberOfFiles());
|
|
for(unsigned long fileNum = 0; fileNum < numFiles; ++fileNum)
|
|
{
|
|
if(!(strcmp(dir.GetFile(fileNum), ".") == 0 ||
|
|
strcmp(dir.GetFile(fileNum), "..") == 0))
|
|
{
|
|
cmsys_stl::string fromPath = source;
|
|
fromPath += "/";
|
|
fromPath += dir.GetFile(fileNum);
|
|
std::string toPath = destination;
|
|
toPath += "/";
|
|
toPath += dir.GetFile(fileNum);
|
|
if(!this->Install(fromPath.c_str(), toPath.c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the requested permissions of the destination directory.
|
|
return this->SetPermissions(destination, permissions_after);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleCopyCommand(std::vector<std::string> const& args)
|
|
{
|
|
cmFileCopier copier(this);
|
|
return copier.Run(args);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
struct cmFileInstaller: public cmFileCopier
|
|
{
|
|
cmFileInstaller(cmFileCommand* command):
|
|
cmFileCopier(command, "INSTALL"),
|
|
InstallType(cmInstallType_FILES),
|
|
Optional(false),
|
|
DestDirLength(0)
|
|
{
|
|
// Installation does not use source permissions by default.
|
|
this->UseSourcePermissions = false;
|
|
// Check whether to copy files always or only if they have changed.
|
|
this->Always =
|
|
cmSystemTools::IsOn(cmSystemTools::GetEnv("CMAKE_INSTALL_ALWAYS"));
|
|
// Get the current manifest.
|
|
this->Manifest =
|
|
this->Makefile->GetSafeDefinition("CMAKE_INSTALL_MANIFEST_FILES");
|
|
}
|
|
~cmFileInstaller()
|
|
{
|
|
// Save the updated install manifest.
|
|
this->Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
|
|
this->Manifest.c_str());
|
|
}
|
|
|
|
protected:
|
|
cmInstallType InstallType;
|
|
bool Optional;
|
|
int DestDirLength;
|
|
std::string Rename;
|
|
|
|
std::string Manifest;
|
|
void ManifestAppend(std::string const& file)
|
|
{
|
|
this->Manifest += ";";
|
|
this->Manifest += file.substr(this->DestDirLength);
|
|
}
|
|
|
|
virtual std::string const& ToName(std::string const& fromName)
|
|
{ return this->Rename.empty()? fromName : this->Rename; }
|
|
|
|
virtual void ReportCopy(const char* toFile, Type type, bool copy)
|
|
{
|
|
std::string message = (copy? "Installing: " : "Up-to-date: ");
|
|
message += toFile;
|
|
this->Makefile->DisplayStatus(message.c_str(), -1);
|
|
if(type != TypeDir)
|
|
{
|
|
// Add the file to the manifest.
|
|
this->ManifestAppend(toFile);
|
|
}
|
|
}
|
|
virtual bool ReportMissing(const char* fromFile)
|
|
{
|
|
return (this->Optional ||
|
|
this->cmFileCopier::ReportMissing(fromFile));
|
|
}
|
|
virtual bool Install(const char* fromFile, const char* toFile)
|
|
{
|
|
// Support installing from empty source to make a directory.
|
|
if(this->InstallType == cmInstallType_DIRECTORY && !*fromFile)
|
|
{
|
|
return this->InstallDirectory(fromFile, toFile, MatchProperties());
|
|
}
|
|
return this->cmFileCopier::Install(fromFile, toFile);
|
|
}
|
|
|
|
virtual bool Parse(std::vector<std::string> const& args);
|
|
enum
|
|
{
|
|
DoingType = DoingLast1,
|
|
DoingRename,
|
|
DoingLast2
|
|
};
|
|
virtual bool CheckKeyword(std::string const& arg);
|
|
virtual bool CheckValue(std::string const& arg);
|
|
virtual void DefaultFilePermissions()
|
|
{
|
|
this->cmFileCopier::DefaultFilePermissions();
|
|
// Add execute permissions based on the target type.
|
|
switch(this->InstallType)
|
|
{
|
|
case cmInstallType_SHARED_LIBRARY:
|
|
case cmInstallType_MODULE_LIBRARY:
|
|
if(this->Makefile->IsOn("CMAKE_INSTALL_SO_NO_EXE"))
|
|
{
|
|
break;
|
|
}
|
|
case cmInstallType_EXECUTABLE:
|
|
case cmInstallType_PROGRAMS:
|
|
this->FilePermissions |= mode_owner_execute;
|
|
this->FilePermissions |= mode_group_execute;
|
|
this->FilePermissions |= mode_world_execute;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
bool GetTargetTypeFromString(const std::string& stype);
|
|
bool HandleInstallDestination();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileInstaller::Parse(std::vector<std::string> const& args)
|
|
{
|
|
if(!this->cmFileCopier::Parse(args))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!this->Rename.empty())
|
|
{
|
|
if(this->InstallType != cmInstallType_FILES &&
|
|
this->InstallType != cmInstallType_PROGRAMS)
|
|
{
|
|
this->FileCommand->SetError("INSTALL option RENAME may be used "
|
|
"only with FILES or PROGRAMS.");
|
|
return false;
|
|
}
|
|
if(this->Files.size() > 1)
|
|
{
|
|
this->FileCommand->SetError("INSTALL option RENAME may be used "
|
|
"only with one file.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(!this->HandleInstallDestination())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileInstaller::CheckKeyword(std::string const& arg)
|
|
{
|
|
if(arg == "TYPE")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingType;
|
|
}
|
|
}
|
|
else if(arg == "FILES")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingFiles;
|
|
}
|
|
}
|
|
else if(arg == "RENAME")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingRename;
|
|
}
|
|
}
|
|
else if(arg == "OPTIONAL")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
this->Doing = DoingNone;
|
|
this->Optional = true;
|
|
}
|
|
}
|
|
else if(arg == "PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->Doing = DoingPermissionsMatch;
|
|
}
|
|
else
|
|
{
|
|
// file(INSTALL) aliases PERMISSIONS to FILE_PERMISSIONS
|
|
this->Doing = DoingPermissionsFile;
|
|
this->UseGivenPermissionsFile = true;
|
|
}
|
|
}
|
|
else if(arg == "DIR_PERMISSIONS")
|
|
{
|
|
if(this->CurrentMatchRule)
|
|
{
|
|
this->NotAfterMatch(arg);
|
|
}
|
|
else
|
|
{
|
|
// file(INSTALL) aliases DIR_PERMISSIONS to DIRECTORY_PERMISSIONS
|
|
this->Doing = DoingPermissionsDir;
|
|
this->UseGivenPermissionsDir = true;
|
|
}
|
|
}
|
|
else if(arg == "COMPONENTS" || arg == "CONFIGURATIONS" ||
|
|
arg == "PROPERTIES")
|
|
{
|
|
cmOStringStream e;
|
|
e << "INSTALL called with old-style " << arg << " argument. "
|
|
<< "This script was generated with an older version of CMake. "
|
|
<< "Re-run this cmake version on your build tree.";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
this->Doing = DoingError;
|
|
}
|
|
else
|
|
{
|
|
return this->cmFileCopier::CheckKeyword(arg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileInstaller::CheckValue(std::string const& arg)
|
|
{
|
|
switch(this->Doing)
|
|
{
|
|
case DoingType:
|
|
if(!this->GetTargetTypeFromString(arg))
|
|
{
|
|
this->Doing = DoingError;
|
|
}
|
|
break;
|
|
case DoingRename:
|
|
this->Rename = arg;
|
|
break;
|
|
default:
|
|
return this->cmFileCopier::CheckValue(arg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileInstaller
|
|
::GetTargetTypeFromString(const std::string& stype)
|
|
{
|
|
if ( stype == "EXECUTABLE" )
|
|
{
|
|
this->InstallType = cmInstallType_EXECUTABLE;
|
|
}
|
|
else if ( stype == "FILE" )
|
|
{
|
|
this->InstallType = cmInstallType_FILES;
|
|
}
|
|
else if ( stype == "PROGRAM" )
|
|
{
|
|
this->InstallType = cmInstallType_PROGRAMS;
|
|
}
|
|
else if ( stype == "STATIC_LIBRARY" )
|
|
{
|
|
this->InstallType = cmInstallType_STATIC_LIBRARY;
|
|
}
|
|
else if ( stype == "SHARED_LIBRARY" )
|
|
{
|
|
this->InstallType = cmInstallType_SHARED_LIBRARY;
|
|
}
|
|
else if ( stype == "MODULE" )
|
|
{
|
|
this->InstallType = cmInstallType_MODULE_LIBRARY;
|
|
}
|
|
else if ( stype == "DIRECTORY" )
|
|
{
|
|
this->InstallType = cmInstallType_DIRECTORY;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "Option TYPE given uknown value \"" << stype << "\".";
|
|
this->FileCommand->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileInstaller::HandleInstallDestination()
|
|
{
|
|
std::string& destination = this->Destination;
|
|
|
|
// allow for / to be a valid destination
|
|
if ( destination.size() < 2 && destination != "/" )
|
|
{
|
|
this->FileCommand->SetError("called with inapropriate arguments. "
|
|
"No DESTINATION provided or .");
|
|
return false;
|
|
}
|
|
|
|
const char* destdir = cmSystemTools::GetEnv("DESTDIR");
|
|
if ( destdir && *destdir )
|
|
{
|
|
std::string sdestdir = destdir;
|
|
cmSystemTools::ConvertToUnixSlashes(sdestdir);
|
|
char ch1 = destination[0];
|
|
char ch2 = destination[1];
|
|
char ch3 = 0;
|
|
if ( destination.size() > 2 )
|
|
{
|
|
ch3 = destination[2];
|
|
}
|
|
int skip = 0;
|
|
if ( ch1 != '/' )
|
|
{
|
|
int relative = 0;
|
|
if (((ch1 >= 'a' && ch1 <= 'z') || (ch1 >= 'A' && ch1 <= 'Z')) &&
|
|
ch2 == ':' )
|
|
{
|
|
// Assume windows
|
|
// let's do some destdir magic:
|
|
skip = 2;
|
|
if ( ch3 != '/' )
|
|
{
|
|
relative = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
relative = 1;
|
|
}
|
|
if ( relative )
|
|
{
|
|
// This is relative path on unix or windows. Since we are doing
|
|
// destdir, this case does not make sense.
|
|
this->FileCommand->SetError(
|
|
"called with relative DESTINATION. This "
|
|
"does not make sense when using DESTDIR. Specify "
|
|
"absolute path or remove DESTDIR environment variable.");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ch2 == '/' )
|
|
{
|
|
// looks like a network path.
|
|
std::string message = "called with network path DESTINATION. This "
|
|
"does not make sense when using DESTDIR. Specify local "
|
|
"absolute path or remove DESTDIR environment variable."
|
|
"\nDESTINATION=\n";
|
|
message += destination;
|
|
this->FileCommand->SetError(message.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
destination = sdestdir + (destination.c_str() + skip);
|
|
this->DestDirLength = int(sdestdir.size());
|
|
}
|
|
|
|
if ( !cmSystemTools::FileExists(destination.c_str()) )
|
|
{
|
|
if ( !cmSystemTools::MakeDirectory(destination.c_str()) )
|
|
{
|
|
std::string errstring = "cannot create directory: " + destination +
|
|
". Maybe need administrative privileges.";
|
|
this->FileCommand->SetError(errstring.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
if ( !cmSystemTools::FileIsDirectory(destination.c_str()) )
|
|
{
|
|
std::string errstring = "INSTALL destination: " + destination +
|
|
" is not a directory.";
|
|
this->FileCommand->SetError(errstring.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
cmFileCommand::HandleRPathChangeCommand(std::vector<std::string> const& args)
|
|
{
|
|
// Evaluate arguments.
|
|
const char* file = 0;
|
|
const char* oldRPath = 0;
|
|
const char* newRPath = 0;
|
|
enum Doing { DoingNone, DoingFile, DoingOld, DoingNew };
|
|
Doing doing = DoingNone;
|
|
for(unsigned int i=1; i < args.size(); ++i)
|
|
{
|
|
if(args[i] == "OLD_RPATH")
|
|
{
|
|
doing = DoingOld;
|
|
}
|
|
else if(args[i] == "NEW_RPATH")
|
|
{
|
|
doing = DoingNew;
|
|
}
|
|
else if(args[i] == "FILE")
|
|
{
|
|
doing = DoingFile;
|
|
}
|
|
else if(doing == DoingFile)
|
|
{
|
|
file = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else if(doing == DoingOld)
|
|
{
|
|
oldRPath = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else if(doing == DoingNew)
|
|
{
|
|
newRPath = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_CHANGE given unknown argument " << args[i];
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
if(!file)
|
|
{
|
|
this->SetError("RPATH_CHANGE not given FILE option.");
|
|
return false;
|
|
}
|
|
if(!oldRPath)
|
|
{
|
|
this->SetError("RPATH_CHANGE not given OLD_RPATH option.");
|
|
return false;
|
|
}
|
|
if(!newRPath)
|
|
{
|
|
this->SetError("RPATH_CHANGE not given NEW_RPATH option.");
|
|
return false;
|
|
}
|
|
if(!cmSystemTools::FileExists(file, true))
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_CHANGE given FILE \"" << file << "\" that does not exist.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
bool success = true;
|
|
cmSystemToolsFileTime* ft = cmSystemTools::FileTimeNew();
|
|
bool have_ft = cmSystemTools::FileTimeGet(file, ft);
|
|
std::string emsg;
|
|
bool changed;
|
|
if(!cmSystemTools::ChangeRPath(file, oldRPath, newRPath, &emsg, &changed))
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_CHANGE could not write new RPATH:\n"
|
|
<< " " << newRPath << "\n"
|
|
<< "to the file:\n"
|
|
<< " " << file << "\n"
|
|
<< emsg;
|
|
this->SetError(e.str().c_str());
|
|
success = false;
|
|
}
|
|
if(success)
|
|
{
|
|
if(changed)
|
|
{
|
|
std::string message = "Set runtime path of \"";
|
|
message += file;
|
|
message += "\" to \"";
|
|
message += newRPath;
|
|
message += "\"";
|
|
this->Makefile->DisplayStatus(message.c_str(), -1);
|
|
}
|
|
if(have_ft)
|
|
{
|
|
cmSystemTools::FileTimeSet(file, ft);
|
|
}
|
|
}
|
|
cmSystemTools::FileTimeDelete(ft);
|
|
return success;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
cmFileCommand::HandleRPathRemoveCommand(std::vector<std::string> const& args)
|
|
{
|
|
// Evaluate arguments.
|
|
const char* file = 0;
|
|
enum Doing { DoingNone, DoingFile };
|
|
Doing doing = DoingNone;
|
|
for(unsigned int i=1; i < args.size(); ++i)
|
|
{
|
|
if(args[i] == "FILE")
|
|
{
|
|
doing = DoingFile;
|
|
}
|
|
else if(doing == DoingFile)
|
|
{
|
|
file = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_REMOVE given unknown argument " << args[i];
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
if(!file)
|
|
{
|
|
this->SetError("RPATH_REMOVE not given FILE option.");
|
|
return false;
|
|
}
|
|
if(!cmSystemTools::FileExists(file, true))
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_REMOVE given FILE \"" << file << "\" that does not exist.";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
bool success = true;
|
|
cmSystemToolsFileTime* ft = cmSystemTools::FileTimeNew();
|
|
bool have_ft = cmSystemTools::FileTimeGet(file, ft);
|
|
std::string emsg;
|
|
bool removed;
|
|
if(!cmSystemTools::RemoveRPath(file, &emsg, &removed))
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_REMOVE could not remove RPATH from file:\n"
|
|
<< " " << file << "\n"
|
|
<< emsg;
|
|
this->SetError(e.str().c_str());
|
|
success = false;
|
|
}
|
|
if(success)
|
|
{
|
|
if(removed)
|
|
{
|
|
std::string message = "Removed runtime path from \"";
|
|
message += file;
|
|
message += "\"";
|
|
this->Makefile->DisplayStatus(message.c_str(), -1);
|
|
}
|
|
if(have_ft)
|
|
{
|
|
cmSystemTools::FileTimeSet(file, ft);
|
|
}
|
|
}
|
|
cmSystemTools::FileTimeDelete(ft);
|
|
return success;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
cmFileCommand::HandleRPathCheckCommand(std::vector<std::string> const& args)
|
|
{
|
|
// Evaluate arguments.
|
|
const char* file = 0;
|
|
const char* rpath = 0;
|
|
enum Doing { DoingNone, DoingFile, DoingRPath };
|
|
Doing doing = DoingNone;
|
|
for(unsigned int i=1; i < args.size(); ++i)
|
|
{
|
|
if(args[i] == "RPATH")
|
|
{
|
|
doing = DoingRPath;
|
|
}
|
|
else if(args[i] == "FILE")
|
|
{
|
|
doing = DoingFile;
|
|
}
|
|
else if(doing == DoingFile)
|
|
{
|
|
file = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else if(doing == DoingRPath)
|
|
{
|
|
rpath = args[i].c_str();
|
|
doing = DoingNone;
|
|
}
|
|
else
|
|
{
|
|
cmOStringStream e;
|
|
e << "RPATH_CHECK given unknown argument " << args[i];
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
if(!file)
|
|
{
|
|
this->SetError("RPATH_CHECK not given FILE option.");
|
|
return false;
|
|
}
|
|
if(!rpath)
|
|
{
|
|
this->SetError("RPATH_CHECK not given RPATH option.");
|
|
return false;
|
|
}
|
|
|
|
// If the file exists but does not have the desired RPath then
|
|
// delete it. This is used during installation to re-install a file
|
|
// if its RPath will change.
|
|
if(cmSystemTools::FileExists(file, true) &&
|
|
!cmSystemTools::CheckRPath(file, rpath))
|
|
{
|
|
cmSystemTools::RemoveFile(file);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleInstallCommand(std::vector<std::string> const& args)
|
|
{
|
|
cmFileInstaller installer(this);
|
|
return installer.Run(args);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleRelativePathCommand(
|
|
std::vector<std::string> const& args)
|
|
{
|
|
if(args.size() != 4 )
|
|
{
|
|
this->SetError("RELATIVE_PATH called with incorrect number of arguments");
|
|
return false;
|
|
}
|
|
|
|
const std::string& outVar = args[1];
|
|
const std::string& directoryName = args[2];
|
|
const std::string& fileName = args[3];
|
|
|
|
if(!cmSystemTools::FileIsFullPath(directoryName.c_str()))
|
|
{
|
|
std::string errstring =
|
|
"RELATIVE_PATH must be passed a full path to the directory: "
|
|
+ directoryName;
|
|
this->SetError(errstring.c_str());
|
|
return false;
|
|
}
|
|
if(!cmSystemTools::FileIsFullPath(fileName.c_str()))
|
|
{
|
|
std::string errstring =
|
|
"RELATIVE_PATH must be passed a full path to the file: "
|
|
+ fileName;
|
|
this->SetError(errstring.c_str());
|
|
return false;
|
|
}
|
|
|
|
std::string res = cmSystemTools::RelativePath(directoryName.c_str(),
|
|
fileName.c_str());
|
|
this->Makefile->AddDefinition(outVar.c_str(),
|
|
res.c_str());
|
|
return true;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleRename(std::vector<std::string> const& args)
|
|
{
|
|
if(args.size() != 3)
|
|
{
|
|
this->SetError("RENAME given incorrect number of arguments.");
|
|
return false;
|
|
}
|
|
|
|
// Compute full path for old and new names.
|
|
std::string oldname = args[1];
|
|
if(!cmsys::SystemTools::FileIsFullPath(oldname.c_str()))
|
|
{
|
|
oldname = this->Makefile->GetCurrentDirectory();
|
|
oldname += "/" + args[1];
|
|
}
|
|
std::string newname = args[2];
|
|
if(!cmsys::SystemTools::FileIsFullPath(newname.c_str()))
|
|
{
|
|
newname = this->Makefile->GetCurrentDirectory();
|
|
newname += "/" + args[2];
|
|
}
|
|
|
|
if(!cmSystemTools::RenameFile(oldname.c_str(), newname.c_str()))
|
|
{
|
|
std::string err = cmSystemTools::GetLastSystemError();
|
|
cmOStringStream e;
|
|
e << "RENAME failed to rename\n"
|
|
<< " " << oldname << "\n"
|
|
<< "to\n"
|
|
<< " " << newname << "\n"
|
|
<< "because: " << err << "\n";
|
|
this->SetError(e.str().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleRemove(std::vector<std::string> const& args,
|
|
bool recurse)
|
|
{
|
|
|
|
std::string message;
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
|
|
i++; // Get rid of subcommand
|
|
for(;i != args.end(); ++i)
|
|
{
|
|
std::string fileName = *i;
|
|
if(!cmsys::SystemTools::FileIsFullPath(fileName.c_str()))
|
|
{
|
|
fileName = this->Makefile->GetCurrentDirectory();
|
|
fileName += "/" + *i;
|
|
}
|
|
|
|
if(cmSystemTools::FileIsDirectory(fileName.c_str()) && recurse)
|
|
{
|
|
cmSystemTools::RemoveADirectory(fileName.c_str());
|
|
}
|
|
else
|
|
{
|
|
cmSystemTools::RemoveFile(fileName.c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmFileCommand::HandleCMakePathCommand(std::vector<std::string>
|
|
const& args,
|
|
bool nativePath)
|
|
{
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
if(args.size() != 3)
|
|
{
|
|
this->SetError("FILE([TO_CMAKE_PATH|TO_NATIVE_PATH] path result) must be "
|
|
"called with exactly three arguments.");
|
|
return false;
|
|
}
|
|
i++; // Get rid of subcommand
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
char pathSep = ';';
|
|
#else
|
|
char pathSep = ':';
|
|
#endif
|
|
std::vector<cmsys::String> path = cmSystemTools::SplitString(i->c_str(),
|
|
pathSep);
|
|
i++;
|
|
const char* var = i->c_str();
|
|
std::string value;
|
|
for(std::vector<cmsys::String>::iterator j = path.begin();
|
|
j != path.end(); ++j)
|
|
{
|
|
if(j != path.begin())
|
|
{
|
|
value += ";";
|
|
}
|
|
if(!nativePath)
|
|
{
|
|
cmSystemTools::ConvertToUnixSlashes(*j);
|
|
}
|
|
else
|
|
{
|
|
*j = cmSystemTools::ConvertToOutputPath(j->c_str());
|
|
// remove double quotes in the path
|
|
cmsys::String& s = *j;
|
|
|
|
if(s.size() > 1 && s[0] == '\"' && s[s.size()-1] == '\"')
|
|
{
|
|
s = s.substr(1,s.size()-2);
|
|
}
|
|
}
|
|
value += *j;
|
|
}
|
|
this->Makefile->AddDefinition(var, value.c_str());
|
|
return true;
|
|
}
|
|
|
|
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
|
|
// Stuff for curl download/upload
|
|
typedef std::vector<char> cmFileCommandVectorOfChar;
|
|
|
|
namespace {
|
|
|
|
size_t
|
|
cmWriteToFileCallback(void *ptr, size_t size, size_t nmemb,
|
|
void *data)
|
|
{
|
|
register int realsize = (int)(size * nmemb);
|
|
std::ofstream* fout = static_cast<std::ofstream*>(data);
|
|
const char* chPtr = static_cast<char*>(ptr);
|
|
fout->write(chPtr, realsize);
|
|
return realsize;
|
|
}
|
|
|
|
|
|
size_t
|
|
cmWriteToMemoryCallback(void *ptr, size_t size, size_t nmemb,
|
|
void *data)
|
|
{
|
|
register int realsize = (int)(size * nmemb);
|
|
cmFileCommandVectorOfChar *vec
|
|
= static_cast<cmFileCommandVectorOfChar*>(data);
|
|
const char* chPtr = static_cast<char*>(ptr);
|
|
vec->insert(vec->end(), chPtr, chPtr + realsize);
|
|
return realsize;
|
|
}
|
|
|
|
|
|
static size_t
|
|
cmFileCommandCurlDebugCallback(CURL *, curl_infotype, char *chPtr,
|
|
size_t size, void *data)
|
|
{
|
|
cmFileCommandVectorOfChar *vec
|
|
= static_cast<cmFileCommandVectorOfChar*>(data);
|
|
vec->insert(vec->end(), chPtr, chPtr + size);
|
|
return size;
|
|
}
|
|
|
|
|
|
class cURLProgressHelper
|
|
{
|
|
public:
|
|
cURLProgressHelper(cmFileCommand *fc, const char *text)
|
|
{
|
|
this->CurrentPercentage = -1;
|
|
this->FileCommand = fc;
|
|
this->Text = text;
|
|
}
|
|
|
|
bool UpdatePercentage(double value, double total, std::string &status)
|
|
{
|
|
int OldPercentage = this->CurrentPercentage;
|
|
|
|
if (total > 0.0)
|
|
{
|
|
this->CurrentPercentage = static_cast<int>(value/total*100.0 + 0.5);
|
|
}
|
|
|
|
bool updated = (OldPercentage != this->CurrentPercentage);
|
|
|
|
if (updated)
|
|
{
|
|
cmOStringStream oss;
|
|
oss << "[" << this->Text << " " << this->CurrentPercentage
|
|
<< "% complete]";
|
|
status = oss.str();
|
|
}
|
|
|
|
return updated;
|
|
}
|
|
|
|
cmFileCommand *GetFileCommand()
|
|
{
|
|
return this->FileCommand;
|
|
}
|
|
|
|
private:
|
|
int CurrentPercentage;
|
|
cmFileCommand *FileCommand;
|
|
std::string Text;
|
|
};
|
|
|
|
|
|
static int
|
|
cmFileDownloadProgressCallback(void *clientp,
|
|
double dltotal, double dlnow,
|
|
double ultotal, double ulnow)
|
|
{
|
|
cURLProgressHelper *helper =
|
|
reinterpret_cast<cURLProgressHelper *>(clientp);
|
|
|
|
static_cast<void>(ultotal);
|
|
static_cast<void>(ulnow);
|
|
|
|
std::string status;
|
|
if (helper->UpdatePercentage(dlnow, dltotal, status))
|
|
{
|
|
cmFileCommand *fc = helper->GetFileCommand();
|
|
cmMakefile *mf = fc->GetMakefile();
|
|
mf->DisplayStatus(status.c_str(), -1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
cmFileUploadProgressCallback(void *clientp,
|
|
double dltotal, double dlnow,
|
|
double ultotal, double ulnow)
|
|
{
|
|
cURLProgressHelper *helper =
|
|
reinterpret_cast<cURLProgressHelper *>(clientp);
|
|
|
|
static_cast<void>(dltotal);
|
|
static_cast<void>(dlnow);
|
|
|
|
std::string status;
|
|
if (helper->UpdatePercentage(ulnow, ultotal, status))
|
|
{
|
|
cmFileCommand *fc = helper->GetFileCommand();
|
|
cmMakefile *mf = fc->GetMakefile();
|
|
mf->DisplayStatus(status.c_str(), -1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
namespace {
|
|
|
|
class cURLEasyGuard
|
|
{
|
|
public:
|
|
cURLEasyGuard(CURL * easy)
|
|
: Easy(easy)
|
|
{}
|
|
|
|
~cURLEasyGuard(void)
|
|
{
|
|
if (this->Easy)
|
|
{
|
|
::curl_easy_cleanup(this->Easy);
|
|
}
|
|
}
|
|
|
|
inline void release(void)
|
|
{
|
|
this->Easy = 0;
|
|
return;
|
|
}
|
|
|
|
private:
|
|
::CURL * Easy;
|
|
};
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
#define check_curl_result(result, errstr) \
|
|
if (result != CURLE_OK) \
|
|
{ \
|
|
std::string e(errstr); \
|
|
e += ::curl_easy_strerror(result); \
|
|
this->SetError(e.c_str()); \
|
|
return false; \
|
|
}
|
|
|
|
|
|
bool
|
|
cmFileCommand::HandleDownloadCommand(std::vector<std::string> const& args)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
if(args.size() < 3)
|
|
{
|
|
this->SetError("DOWNLOAD must be called with at least three arguments.");
|
|
return false;
|
|
}
|
|
++i; // Get rid of subcommand
|
|
std::string url = *i;
|
|
++i;
|
|
std::string file = *i;
|
|
++i;
|
|
|
|
long timeout = 0;
|
|
long inactivity_timeout = 0;
|
|
std::string verboseLog;
|
|
std::string statusVar;
|
|
std::string expectedMD5sum;
|
|
bool showProgress = false;
|
|
|
|
while(i != args.end())
|
|
{
|
|
if(*i == "TIMEOUT")
|
|
{
|
|
++i;
|
|
if(i != args.end())
|
|
{
|
|
timeout = atol(i->c_str());
|
|
}
|
|
else
|
|
{
|
|
this->SetError("DOWNLOAD missing time for TIMEOUT.");
|
|
return false;
|
|
}
|
|
}
|
|
else if(*i == "INACTIVITY_TIMEOUT")
|
|
{
|
|
++i;
|
|
if(i != args.end())
|
|
{
|
|
inactivity_timeout = atol(i->c_str());
|
|
}
|
|
else
|
|
{
|
|
this->SetError("DOWNLOAD missing time for INACTIVITY_TIMEOUT.");
|
|
return false;
|
|
}
|
|
}
|
|
else if(*i == "LOG")
|
|
{
|
|
++i;
|
|
if( i == args.end())
|
|
{
|
|
this->SetError("DOWNLOAD missing VAR for LOG.");
|
|
return false;
|
|
}
|
|
verboseLog = *i;
|
|
}
|
|
else if(*i == "STATUS")
|
|
{
|
|
++i;
|
|
if( i == args.end())
|
|
{
|
|
this->SetError("DOWNLOAD missing VAR for STATUS.");
|
|
return false;
|
|
}
|
|
statusVar = *i;
|
|
}
|
|
else if(*i == "EXPECTED_MD5")
|
|
{
|
|
++i;
|
|
if( i == args.end())
|
|
{
|
|
this->SetError("DOWNLOAD missing sum value for EXPECTED_MD5.");
|
|
return false;
|
|
}
|
|
expectedMD5sum = cmSystemTools::LowerCase(*i);
|
|
}
|
|
else if(*i == "SHOW_PROGRESS")
|
|
{
|
|
showProgress = true;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
// If file exists already, and caller specified an expected md5 sum,
|
|
// and the existing file already has the expected md5 sum, then simply
|
|
// return.
|
|
//
|
|
if(cmSystemTools::FileExists(file.c_str()) &&
|
|
!expectedMD5sum.empty())
|
|
{
|
|
char computedMD5[32];
|
|
|
|
if (!cmSystemTools::ComputeFileMD5(file.c_str(), computedMD5))
|
|
{
|
|
this->SetError("DOWNLOAD cannot compute MD5 sum on pre-existing file");
|
|
return false;
|
|
}
|
|
|
|
std::string actualMD5sum = cmSystemTools::LowerCase(
|
|
std::string(computedMD5, 32));
|
|
|
|
if (expectedMD5sum == actualMD5sum)
|
|
{
|
|
if(statusVar.size())
|
|
{
|
|
cmOStringStream result;
|
|
result << (int)0 << ";\""
|
|
"returning early: file already exists with expected MD5 sum\"";
|
|
this->Makefile->AddDefinition(statusVar.c_str(),
|
|
result.str().c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Make sure parent directory exists so we can write to the file
|
|
// as we receive downloaded bits from curl...
|
|
//
|
|
std::string dir = cmSystemTools::GetFilenamePath(file.c_str());
|
|
if(!cmSystemTools::FileExists(dir.c_str()) &&
|
|
!cmSystemTools::MakeDirectory(dir.c_str()))
|
|
{
|
|
std::string errstring = "DOWNLOAD error: cannot create directory '"
|
|
+ dir + "' - Specify file by full path name and verify that you "
|
|
"have directory creation and file write privileges.";
|
|
this->SetError(errstring.c_str());
|
|
return false;
|
|
}
|
|
|
|
std::ofstream fout(file.c_str(), std::ios::binary);
|
|
if(!fout)
|
|
{
|
|
this->SetError("DOWNLOAD cannot open file for write.");
|
|
return false;
|
|
}
|
|
|
|
::CURL *curl;
|
|
::curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
curl = ::curl_easy_init();
|
|
if(!curl)
|
|
{
|
|
this->SetError("DOWNLOAD error initializing curl.");
|
|
return false;
|
|
}
|
|
|
|
cURLEasyGuard g_curl(curl);
|
|
|
|
::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
check_curl_result(res, "DOWNLOAD cannot set url: ");
|
|
|
|
// enable HTTP ERROR parsing
|
|
res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
|
check_curl_result(res, "DOWNLOAD cannot set http failure option: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
|
|
cmWriteToFileCallback);
|
|
check_curl_result(res, "DOWNLOAD cannot set write function: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
|
|
cmFileCommandCurlDebugCallback);
|
|
check_curl_result(res, "DOWNLOAD cannot set debug function: ");
|
|
|
|
cmFileCommandVectorOfChar chunkDebug;
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&fout);
|
|
check_curl_result(res, "DOWNLOAD cannot set write data: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
|
|
check_curl_result(res, "DOWNLOAD cannot set debug data: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
check_curl_result(res, "DOWNLOAD cannot set follow-redirect option: ");
|
|
|
|
if(verboseLog.size())
|
|
{
|
|
res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
|
check_curl_result(res, "DOWNLOAD cannot set verbose: ");
|
|
}
|
|
|
|
if(timeout > 0)
|
|
{
|
|
res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout );
|
|
check_curl_result(res, "DOWNLOAD cannot set timeout: ");
|
|
}
|
|
|
|
if(inactivity_timeout > 0)
|
|
{
|
|
// Give up if there is no progress for a long time.
|
|
::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
|
|
::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
|
|
}
|
|
|
|
// Need the progress helper's scope to last through the duration of
|
|
// the curl_easy_perform call... so this object is declared at function
|
|
// scope intentionally, rather than inside the "if(showProgress)"
|
|
// block...
|
|
//
|
|
cURLProgressHelper helper(this, "download");
|
|
|
|
if(showProgress)
|
|
{
|
|
res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
|
check_curl_result(res, "DOWNLOAD cannot set noprogress value: ");
|
|
|
|
res = ::curl_easy_setopt(curl,
|
|
CURLOPT_PROGRESSFUNCTION, cmFileDownloadProgressCallback);
|
|
check_curl_result(res, "DOWNLOAD cannot set progress function: ");
|
|
|
|
res = ::curl_easy_setopt(curl,
|
|
CURLOPT_PROGRESSDATA, reinterpret_cast<void*>(&helper));
|
|
check_curl_result(res, "DOWNLOAD cannot set progress data: ");
|
|
}
|
|
|
|
res = ::curl_easy_perform(curl);
|
|
|
|
/* always cleanup */
|
|
g_curl.release();
|
|
::curl_easy_cleanup(curl);
|
|
|
|
if(statusVar.size())
|
|
{
|
|
cmOStringStream result;
|
|
result << (int)res << ";\"" << ::curl_easy_strerror(res) << "\"";
|
|
this->Makefile->AddDefinition(statusVar.c_str(),
|
|
result.str().c_str());
|
|
}
|
|
|
|
::curl_global_cleanup();
|
|
|
|
// Explicitly flush/close so we can measure the md5 accurately.
|
|
//
|
|
fout.flush();
|
|
fout.close();
|
|
|
|
// Verify MD5 sum if requested:
|
|
//
|
|
if (!expectedMD5sum.empty())
|
|
{
|
|
char computedMD5[32];
|
|
|
|
if (!cmSystemTools::ComputeFileMD5(file.c_str(), computedMD5))
|
|
{
|
|
this->SetError("DOWNLOAD cannot compute MD5 sum on downloaded file");
|
|
return false;
|
|
}
|
|
|
|
std::string actualMD5sum = cmSystemTools::LowerCase(
|
|
std::string(computedMD5, 32));
|
|
|
|
if (expectedMD5sum != actualMD5sum)
|
|
{
|
|
cmOStringStream oss;
|
|
oss << "DOWNLOAD MD5 mismatch" << std::endl
|
|
<< " for file: [" << file << "]" << std::endl
|
|
<< " expected MD5 sum: [" << expectedMD5sum << "]" << std::endl
|
|
<< " actual MD5 sum: [" << actualMD5sum << "]" << std::endl
|
|
;
|
|
this->SetError(oss.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(chunkDebug.size())
|
|
{
|
|
chunkDebug.push_back(0);
|
|
if(CURLE_OPERATION_TIMEOUTED == res)
|
|
{
|
|
std::string output = &*chunkDebug.begin();
|
|
|
|
if(verboseLog.size())
|
|
{
|
|
this->Makefile->AddDefinition(verboseLog.c_str(),
|
|
&*chunkDebug.begin());
|
|
}
|
|
}
|
|
|
|
this->Makefile->AddDefinition(verboseLog.c_str(),
|
|
&*chunkDebug.begin());
|
|
}
|
|
|
|
return true;
|
|
#else
|
|
this->SetError("DOWNLOAD not supported by bootstrap cmake.");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
bool
|
|
cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
if(args.size() < 3)
|
|
{
|
|
this->SetError("UPLOAD must be called with at least three arguments.");
|
|
return false;
|
|
}
|
|
std::vector<std::string>::const_iterator i = args.begin();
|
|
++i;
|
|
std::string filename = *i;
|
|
++i;
|
|
std::string url = *i;
|
|
++i;
|
|
|
|
long timeout = 0;
|
|
long inactivity_timeout = 0;
|
|
std::string logVar;
|
|
std::string statusVar;
|
|
bool showProgress = false;
|
|
|
|
while(i != args.end())
|
|
{
|
|
if(*i == "TIMEOUT")
|
|
{
|
|
++i;
|
|
if(i != args.end())
|
|
{
|
|
timeout = atol(i->c_str());
|
|
}
|
|
else
|
|
{
|
|
this->SetError("UPLOAD missing time for TIMEOUT.");
|
|
return false;
|
|
}
|
|
}
|
|
else if(*i == "INACTIVITY_TIMEOUT")
|
|
{
|
|
++i;
|
|
if(i != args.end())
|
|
{
|
|
inactivity_timeout = atol(i->c_str());
|
|
}
|
|
else
|
|
{
|
|
this->SetError("UPLOAD missing time for INACTIVITY_TIMEOUT.");
|
|
return false;
|
|
}
|
|
}
|
|
else if(*i == "LOG")
|
|
{
|
|
++i;
|
|
if( i == args.end())
|
|
{
|
|
this->SetError("UPLOAD missing VAR for LOG.");
|
|
return false;
|
|
}
|
|
logVar = *i;
|
|
}
|
|
else if(*i == "STATUS")
|
|
{
|
|
++i;
|
|
if( i == args.end())
|
|
{
|
|
this->SetError("UPLOAD missing VAR for STATUS.");
|
|
return false;
|
|
}
|
|
statusVar = *i;
|
|
}
|
|
else if(*i == "SHOW_PROGRESS")
|
|
{
|
|
showProgress = true;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
// Open file for reading:
|
|
//
|
|
FILE *fin = fopen(filename.c_str(), "rb");
|
|
if(!fin)
|
|
{
|
|
std::string errStr = "UPLOAD cannot open file '";
|
|
errStr += filename + "' for reading.";
|
|
this->SetError(errStr.c_str());
|
|
return false;
|
|
}
|
|
|
|
struct stat st;
|
|
if(::stat(filename.c_str(), &st))
|
|
{
|
|
std::string errStr = "UPLOAD cannot stat file '";
|
|
errStr += filename + "'.";
|
|
this->SetError(errStr.c_str());
|
|
fclose(fin);
|
|
return false;
|
|
}
|
|
|
|
::CURL *curl;
|
|
::curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
curl = ::curl_easy_init();
|
|
if(!curl)
|
|
{
|
|
this->SetError("UPLOAD error initializing curl.");
|
|
fclose(fin);
|
|
return false;
|
|
}
|
|
|
|
cURLEasyGuard g_curl(curl);
|
|
|
|
// enable HTTP ERROR parsing
|
|
::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
|
|
|
// enable uploading
|
|
res = ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
|
|
check_curl_result(res, "UPLOAD cannot set upload flag: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
check_curl_result(res, "UPLOAD cannot set url: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
|
|
cmWriteToMemoryCallback);
|
|
check_curl_result(res, "UPLOAD cannot set write function: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
|
|
cmFileCommandCurlDebugCallback);
|
|
check_curl_result(res, "UPLOAD cannot set debug function: ");
|
|
|
|
cmFileCommandVectorOfChar chunkResponse;
|
|
cmFileCommandVectorOfChar chunkDebug;
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunkResponse);
|
|
check_curl_result(res, "UPLOAD cannot set write data: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void *)&chunkDebug);
|
|
check_curl_result(res, "UPLOAD cannot set debug data: ");
|
|
|
|
res = ::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
check_curl_result(res, "UPLOAD cannot set follow-redirect option: ");
|
|
|
|
if(logVar.size())
|
|
{
|
|
res = ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
|
check_curl_result(res, "UPLOAD cannot set verbose: ");
|
|
}
|
|
|
|
if(timeout > 0)
|
|
{
|
|
res = ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout );
|
|
check_curl_result(res, "UPLOAD cannot set timeout: ");
|
|
}
|
|
|
|
if(inactivity_timeout > 0)
|
|
{
|
|
// Give up if there is no progress for a long time.
|
|
::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
|
|
::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, inactivity_timeout);
|
|
}
|
|
|
|
// Need the progress helper's scope to last through the duration of
|
|
// the curl_easy_perform call... so this object is declared at function
|
|
// scope intentionally, rather than inside the "if(showProgress)"
|
|
// block...
|
|
//
|
|
cURLProgressHelper helper(this, "upload");
|
|
|
|
if(showProgress)
|
|
{
|
|
res = ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
|
check_curl_result(res, "UPLOAD cannot set noprogress value: ");
|
|
|
|
res = ::curl_easy_setopt(curl,
|
|
CURLOPT_PROGRESSFUNCTION, cmFileUploadProgressCallback);
|
|
check_curl_result(res, "UPLOAD cannot set progress function: ");
|
|
|
|
res = ::curl_easy_setopt(curl,
|
|
CURLOPT_PROGRESSDATA, reinterpret_cast<void*>(&helper));
|
|
check_curl_result(res, "UPLOAD cannot set progress data: ");
|
|
}
|
|
|
|
// now specify which file to upload
|
|
res = ::curl_easy_setopt(curl, CURLOPT_INFILE, fin);
|
|
check_curl_result(res, "UPLOAD cannot set input file: ");
|
|
|
|
// and give the size of the upload (optional)
|
|
res = ::curl_easy_setopt(curl,
|
|
CURLOPT_INFILESIZE, static_cast<long>(st.st_size));
|
|
check_curl_result(res, "UPLOAD cannot set input file size: ");
|
|
|
|
res = ::curl_easy_perform(curl);
|
|
|
|
/* always cleanup */
|
|
g_curl.release();
|
|
::curl_easy_cleanup(curl);
|
|
|
|
if(statusVar.size())
|
|
{
|
|
cmOStringStream result;
|
|
result << (int)res << ";\"" << ::curl_easy_strerror(res) << "\"";
|
|
this->Makefile->AddDefinition(statusVar.c_str(),
|
|
result.str().c_str());
|
|
}
|
|
|
|
::curl_global_cleanup();
|
|
|
|
fclose(fin);
|
|
fin = NULL;
|
|
|
|
if(logVar.size())
|
|
{
|
|
std::string log;
|
|
|
|
if(chunkResponse.size())
|
|
{
|
|
chunkResponse.push_back(0);
|
|
log += "Response:\n";
|
|
log += &*chunkResponse.begin();
|
|
log += "\n";
|
|
}
|
|
|
|
if(chunkDebug.size())
|
|
{
|
|
chunkDebug.push_back(0);
|
|
log += "Debug:\n";
|
|
log += &*chunkDebug.begin();
|
|
log += "\n";
|
|
}
|
|
|
|
this->Makefile->AddDefinition(logVar.c_str(), log.c_str());
|
|
}
|
|
|
|
return true;
|
|
#else
|
|
this->SetError("UPLOAD not supported by bootstrap cmake.");
|
|
return false;
|
|
#endif
|
|
}
|