mirror of
https://github.com/reactos/CMake.git
synced 2024-12-03 00:57:25 +00:00
7b1f966a6c
Several CMake operations need to replace files in rapid succession. This commonly fails on Windows due to filesystem lock behavior so we have retry loops. No matter how many times we retry or how long we delay there will inevitably be someone with an environment that needs more. Make the retry count and delay configurable in the Windows Registry keys: {HKCU,HKLM}/Software/Kitware/CMake/Config in DWORD values FilesystemRetryCount = Number of tries FilesystemRetryDelay = Delay in milliseconds between tries Leave the feature undocumented for now to see how it goes.
2892 lines
76 KiB
C++
2892 lines
76 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.
|
|
============================================================================*/
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
# define _WIN32_WINNT 0x0400 /* for wincrypt.h */
|
|
#endif
|
|
#include "cmSystemTools.h"
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#ifdef __QNX__
|
|
# include <malloc.h> /* for malloc/free on QNX */
|
|
#endif
|
|
#include <cmsys/Glob.hxx>
|
|
#include <cmsys/RegularExpression.hxx>
|
|
#include <cmsys/Directory.hxx>
|
|
#include <cmsys/System.h>
|
|
#include <cmsys/Encoding.hxx>
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
# include "cmArchiveWrite.h"
|
|
# include <cm_libarchive.h>
|
|
# include <cmsys/Terminal.h>
|
|
#endif
|
|
#include <cmsys/stl/algorithm>
|
|
#include <cmsys/FStream.hxx>
|
|
|
|
#if defined(_WIN32)
|
|
# include <windows.h>
|
|
# include <wincrypt.h>
|
|
#else
|
|
# include <sys/time.h>
|
|
# include <sys/types.h>
|
|
# include <unistd.h>
|
|
# include <utime.h>
|
|
# include <sys/wait.h>
|
|
#endif
|
|
|
|
#if defined(__APPLE__)
|
|
# include <mach-o/dyld.h>
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#if defined(_WIN32) && \
|
|
(defined(_MSC_VER) || defined(__WATCOMC__) || \
|
|
defined(__BORLANDC__) || defined(__MINGW32__))
|
|
# include <io.h>
|
|
#endif
|
|
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
# include <fcntl.h>
|
|
# include "cmCryptoHash.h"
|
|
#endif
|
|
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
# include "cmELF.h"
|
|
#endif
|
|
|
|
class cmSystemToolsFileTime
|
|
{
|
|
public:
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
FILETIME timeCreation;
|
|
FILETIME timeLastAccess;
|
|
FILETIME timeLastWrite;
|
|
#else
|
|
struct utimbuf timeBuf;
|
|
#endif
|
|
};
|
|
|
|
#if defined(__sgi) && !defined(__GNUC__)
|
|
# pragma set woff 1375 /* base class destructor not virtual */
|
|
#endif
|
|
|
|
#if !defined(HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE)
|
|
// For GetEnvironmentVariables
|
|
# if defined(_WIN32)
|
|
extern __declspec( dllimport ) char** environ;
|
|
# else
|
|
extern char** environ;
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
class cmSystemToolsWindowsHandle
|
|
{
|
|
public:
|
|
cmSystemToolsWindowsHandle(HANDLE h): handle_(h) {}
|
|
~cmSystemToolsWindowsHandle()
|
|
{
|
|
if(this->handle_ != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(this->handle_);
|
|
}
|
|
}
|
|
operator bool() const { return this->handle_ != INVALID_HANDLE_VALUE; }
|
|
bool operator !() const { return this->handle_ == INVALID_HANDLE_VALUE; }
|
|
operator HANDLE() const { return this->handle_; }
|
|
private:
|
|
HANDLE handle_;
|
|
};
|
|
#elif defined(__APPLE__)
|
|
#include <crt_externs.h>
|
|
#define environ (*_NSGetEnviron())
|
|
#endif
|
|
|
|
bool cmSystemTools::s_RunCommandHideConsole = false;
|
|
bool cmSystemTools::s_DisableRunCommandOutput = false;
|
|
bool cmSystemTools::s_ErrorOccured = false;
|
|
bool cmSystemTools::s_FatalErrorOccured = false;
|
|
bool cmSystemTools::s_DisableMessages = false;
|
|
bool cmSystemTools::s_ForceUnixPaths = false;
|
|
|
|
void (*cmSystemTools::s_ErrorCallback)(const char*, const char*,
|
|
bool&, void*);
|
|
void (*cmSystemTools::s_StdoutCallback)(const char*, int len, void*);
|
|
void* cmSystemTools::s_ErrorCallbackClientData = 0;
|
|
void* cmSystemTools::s_StdoutCallbackClientData = 0;
|
|
bool (*cmSystemTools::s_InterruptCallback)(void*);
|
|
void* cmSystemTools::s_InterruptCallbackClientData = 0;
|
|
|
|
// replace replace with with as many times as it shows up in source.
|
|
// write the result into source.
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
void cmSystemTools::ExpandRegistryValues(std::string& source, KeyWOW64 view)
|
|
{
|
|
// Regular expression to match anything inside [...] that begins in HKEY.
|
|
// Note that there is a special rule for regular expressions to match a
|
|
// close square-bracket inside a list delimited by square brackets.
|
|
// The "[^]]" part of this expression will match any character except
|
|
// a close square-bracket. The ']' character must be the first in the
|
|
// list of characters inside the [^...] block of the expression.
|
|
cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
|
|
|
|
// check for black line or comment
|
|
while (regEntry.find(source))
|
|
{
|
|
// the arguments are the second match
|
|
std::string key = regEntry.match(1);
|
|
std::string val;
|
|
if (ReadRegistryValue(key.c_str(), val, view))
|
|
{
|
|
std::string reg = "[";
|
|
reg += key + "]";
|
|
cmSystemTools::ReplaceString(source, reg.c_str(), val.c_str());
|
|
}
|
|
else
|
|
{
|
|
std::string reg = "[";
|
|
reg += key + "]";
|
|
cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
void cmSystemTools::ExpandRegistryValues(std::string& source, KeyWOW64)
|
|
{
|
|
cmsys::RegularExpression regEntry("\\[(HKEY[^]]*)\\]");
|
|
while (regEntry.find(source))
|
|
{
|
|
// the arguments are the second match
|
|
std::string key = regEntry.match(1);
|
|
std::string val;
|
|
std::string reg = "[";
|
|
reg += key + "]";
|
|
cmSystemTools::ReplaceString(source, reg.c_str(), "/registry");
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
std::string cmSystemTools::EscapeQuotes(const char* str)
|
|
{
|
|
std::string result = "";
|
|
for(const char* ch = str; *ch != '\0'; ++ch)
|
|
{
|
|
if(*ch == '"')
|
|
{
|
|
result += '\\';
|
|
}
|
|
result += *ch;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string cmSystemTools::HelpFileName(std::string name)
|
|
{
|
|
cmSystemTools::ReplaceString(name, "<", "");
|
|
cmSystemTools::ReplaceString(name, ">", "");
|
|
return name;
|
|
}
|
|
|
|
std::string cmSystemTools::TrimWhitespace(const std::string& s)
|
|
{
|
|
std::string::const_iterator start = s.begin();
|
|
while(start != s.end() && *start <= ' ')
|
|
++start;
|
|
if (start == s.end())
|
|
return "";
|
|
|
|
std::string::const_iterator stop = s.end()-1;
|
|
while(*stop <= ' ')
|
|
--stop;
|
|
return std::string(start, stop+1);
|
|
}
|
|
|
|
void cmSystemTools::Error(const char* m1, const char* m2,
|
|
const char* m3, const char* m4)
|
|
{
|
|
std::string message = "CMake Error: ";
|
|
if(m1)
|
|
{
|
|
message += m1;
|
|
}
|
|
if(m2)
|
|
{
|
|
message += m2;
|
|
}
|
|
if(m3)
|
|
{
|
|
message += m3;
|
|
}
|
|
if(m4)
|
|
{
|
|
message += m4;
|
|
}
|
|
cmSystemTools::s_ErrorOccured = true;
|
|
cmSystemTools::Message(message.c_str(),"Error");
|
|
}
|
|
|
|
void cmSystemTools::SetInterruptCallback(InterruptCallback f, void* clientData)
|
|
{
|
|
s_InterruptCallback = f;
|
|
s_InterruptCallbackClientData = clientData;
|
|
}
|
|
|
|
bool cmSystemTools::GetInterruptFlag()
|
|
{
|
|
if(s_InterruptCallback)
|
|
{
|
|
return (*s_InterruptCallback)(s_InterruptCallbackClientData);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cmSystemTools::SetErrorCallback(ErrorCallback f, void* clientData)
|
|
{
|
|
s_ErrorCallback = f;
|
|
s_ErrorCallbackClientData = clientData;
|
|
}
|
|
|
|
void cmSystemTools::SetStdoutCallback(StdoutCallback f, void* clientData)
|
|
{
|
|
s_StdoutCallback = f;
|
|
s_StdoutCallbackClientData = clientData;
|
|
}
|
|
|
|
void cmSystemTools::Stdout(const char* s)
|
|
{
|
|
if(s_StdoutCallback)
|
|
{
|
|
(*s_StdoutCallback)(s, static_cast<int>(strlen(s)),
|
|
s_StdoutCallbackClientData);
|
|
}
|
|
else
|
|
{
|
|
std::cout << s;
|
|
std::cout.flush();
|
|
}
|
|
}
|
|
|
|
void cmSystemTools::Stderr(const char* s, int length)
|
|
{
|
|
std::cerr.write(s, length);
|
|
std::cerr.flush();
|
|
}
|
|
|
|
void cmSystemTools::Stdout(const char* s, int length)
|
|
{
|
|
if(s_StdoutCallback)
|
|
{
|
|
(*s_StdoutCallback)(s, length, s_StdoutCallbackClientData);
|
|
}
|
|
else
|
|
{
|
|
std::cout.write(s, length);
|
|
std::cout.flush();
|
|
}
|
|
}
|
|
|
|
void cmSystemTools::Message(const char* m1, const char *title)
|
|
{
|
|
if(s_DisableMessages)
|
|
{
|
|
return;
|
|
}
|
|
if(s_ErrorCallback)
|
|
{
|
|
(*s_ErrorCallback)(m1, title, s_DisableMessages,
|
|
s_ErrorCallbackClientData);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << m1 << std::endl << std::flush;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void cmSystemTools::ReportLastSystemError(const char* msg)
|
|
{
|
|
std::string m = msg;
|
|
m += ": System Error: ";
|
|
m += Superclass::GetLastSystemError();
|
|
cmSystemTools::Error(m.c_str());
|
|
}
|
|
|
|
bool cmSystemTools::IsInternallyOn(const char* val)
|
|
{
|
|
if (!val)
|
|
{
|
|
return false;
|
|
}
|
|
std::basic_string<char> v = val;
|
|
|
|
for(std::basic_string<char>::iterator c = v.begin();
|
|
c != v.end(); c++)
|
|
{
|
|
*c = static_cast<char>(toupper(*c));
|
|
}
|
|
return (v == "I_ON" || v == "i_on");
|
|
}
|
|
|
|
bool cmSystemTools::IsOn(const char* val)
|
|
{
|
|
if (!val)
|
|
{
|
|
return false;
|
|
}
|
|
std::basic_string<char> v = val;
|
|
|
|
for(std::basic_string<char>::iterator c = v.begin();
|
|
c != v.end(); c++)
|
|
{
|
|
*c = static_cast<char>(toupper(*c));
|
|
}
|
|
return (v == "ON" || v == "1" || v == "YES" || v == "TRUE" || v == "Y");
|
|
}
|
|
|
|
bool cmSystemTools::IsNOTFOUND(const char* val)
|
|
{
|
|
if(strcmp(val, "NOTFOUND") == 0)
|
|
{
|
|
return true;
|
|
}
|
|
return cmHasLiteralSuffix(val, "-NOTFOUND");
|
|
}
|
|
|
|
|
|
bool cmSystemTools::IsOff(const char* val)
|
|
{
|
|
if (!val || strlen(val) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
std::basic_string<char> v = val;
|
|
|
|
for(std::basic_string<char>::iterator c = v.begin();
|
|
c != v.end(); c++)
|
|
{
|
|
*c = static_cast<char>(toupper(*c));
|
|
}
|
|
return (v == "OFF" || v == "0" || v == "NO" || v == "FALSE" ||
|
|
v == "N" || cmSystemTools::IsNOTFOUND(v.c_str()) || v == "IGNORE");
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmSystemTools::ParseWindowsCommandLine(const char* command,
|
|
std::vector<std::string>& args)
|
|
{
|
|
// See the MSDN document "Parsing C Command-Line Arguments" at
|
|
// http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx for rules
|
|
// of parsing the windows command line.
|
|
|
|
bool in_argument = false;
|
|
bool in_quotes = false;
|
|
int backslashes = 0;
|
|
std::string arg;
|
|
for(const char* c = command;*c; ++c)
|
|
{
|
|
if(*c == '\\')
|
|
{
|
|
++backslashes;
|
|
in_argument = true;
|
|
}
|
|
else if(*c == '"')
|
|
{
|
|
int backslash_pairs = backslashes >> 1;
|
|
int backslash_escaped = backslashes & 1;
|
|
arg.append(backslash_pairs, '\\');
|
|
backslashes = 0;
|
|
if(backslash_escaped)
|
|
{
|
|
/* An odd number of backslashes precede this quote.
|
|
It is escaped. */
|
|
arg.append(1, '"');
|
|
}
|
|
else
|
|
{
|
|
/* An even number of backslashes precede this quote.
|
|
It is not escaped. */
|
|
in_quotes = !in_quotes;
|
|
}
|
|
in_argument = true;
|
|
}
|
|
else
|
|
{
|
|
arg.append(backslashes, '\\');
|
|
backslashes = 0;
|
|
if(isspace(*c))
|
|
{
|
|
if(in_quotes)
|
|
{
|
|
arg.append(1, *c);
|
|
}
|
|
else if(in_argument)
|
|
{
|
|
args.push_back(arg);
|
|
arg = "";
|
|
in_argument = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
in_argument = true;
|
|
arg.append(1, *c);
|
|
}
|
|
}
|
|
}
|
|
arg.append(backslashes, '\\');
|
|
if(in_argument)
|
|
{
|
|
args.push_back(arg);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
class cmSystemToolsArgV
|
|
{
|
|
char** ArgV;
|
|
public:
|
|
cmSystemToolsArgV(char** argv): ArgV(argv) {}
|
|
~cmSystemToolsArgV()
|
|
{
|
|
for(char** arg = this->ArgV; arg && *arg; ++arg)
|
|
{
|
|
free(*arg);
|
|
}
|
|
free(this->ArgV);
|
|
}
|
|
void Store(std::vector<std::string>& args) const
|
|
{
|
|
for(char** arg = this->ArgV; arg && *arg; ++arg)
|
|
{
|
|
args.push_back(*arg);
|
|
}
|
|
}
|
|
void Store(std::vector<cmStdString>& args) const
|
|
{
|
|
for(char** arg = this->ArgV; arg && *arg; ++arg)
|
|
{
|
|
args.push_back(*arg);
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmSystemTools::ParseUnixCommandLine(const char* command,
|
|
std::vector<std::string>& args)
|
|
{
|
|
// Invoke the underlying parser.
|
|
cmSystemToolsArgV argv = cmsysSystem_Parse_CommandForUnix(command, 0);
|
|
argv.Store(args);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmSystemTools::ParseUnixCommandLine(const char* command,
|
|
std::vector<cmStdString>& args)
|
|
{
|
|
// Invoke the underlying parser.
|
|
cmSystemToolsArgV argv = cmsysSystem_Parse_CommandForUnix(command, 0);
|
|
argv.Store(args);
|
|
}
|
|
|
|
std::string cmSystemTools::EscapeWindowsShellArgument(const char* arg,
|
|
int shell_flags)
|
|
{
|
|
char local_buffer[1024];
|
|
char* buffer = local_buffer;
|
|
int size = cmsysSystem_Shell_GetArgumentSizeForWindows(arg, shell_flags);
|
|
if(size > 1024)
|
|
{
|
|
buffer = new char[size];
|
|
}
|
|
cmsysSystem_Shell_GetArgumentForWindows(arg, buffer, shell_flags);
|
|
std::string result(buffer);
|
|
if(buffer != local_buffer)
|
|
{
|
|
delete [] buffer;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<cmStdString> cmSystemTools::ParseArguments(const char* command)
|
|
{
|
|
std::vector<cmStdString> args;
|
|
std::string arg;
|
|
|
|
bool win_path = false;
|
|
|
|
if ((command[0] != '/' && command[1] == ':' && command[2] == '\\') ||
|
|
(command[0] == '\"' && command[1] != '/' && command[2] == ':'
|
|
&& command[3] == '\\') ||
|
|
(command[0] == '\'' && command[1] != '/' && command[2] == ':'
|
|
&& command[3] == '\\') ||
|
|
(command[0] == '\\' && command[1] == '\\'))
|
|
{
|
|
win_path = true;
|
|
}
|
|
// Split the command into an argv array.
|
|
for(const char* c = command; *c;)
|
|
{
|
|
// Skip over whitespace.
|
|
while(*c == ' ' || *c == '\t')
|
|
{
|
|
++c;
|
|
}
|
|
arg = "";
|
|
if(*c == '"')
|
|
{
|
|
// Parse a quoted argument.
|
|
++c;
|
|
while(*c && *c != '"')
|
|
{
|
|
arg.append(1, *c);
|
|
++c;
|
|
}
|
|
if(*c)
|
|
{
|
|
++c;
|
|
}
|
|
args.push_back(arg);
|
|
}
|
|
else if(*c == '\'')
|
|
{
|
|
// Parse a quoted argument.
|
|
++c;
|
|
while(*c && *c != '\'')
|
|
{
|
|
arg.append(1, *c);
|
|
++c;
|
|
}
|
|
if(*c)
|
|
{
|
|
++c;
|
|
}
|
|
args.push_back(arg);
|
|
}
|
|
else if(*c)
|
|
{
|
|
// Parse an unquoted argument.
|
|
while(*c && *c != ' ' && *c != '\t')
|
|
{
|
|
if(*c == '\\' && !win_path)
|
|
{
|
|
++c;
|
|
if(*c)
|
|
{
|
|
arg.append(1, *c);
|
|
++c;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arg.append(1, *c);
|
|
++c;
|
|
}
|
|
}
|
|
args.push_back(arg);
|
|
}
|
|
}
|
|
|
|
return args;
|
|
}
|
|
|
|
|
|
bool cmSystemTools::RunSingleCommand(std::vector<cmStdString>const& command,
|
|
std::string* output ,
|
|
int* retVal , const char* dir ,
|
|
OutputOption outputflag ,
|
|
double timeout )
|
|
{
|
|
std::vector<std::string> cmd;
|
|
for(std::vector<cmStdString>::const_iterator i = command.begin();
|
|
i != command.end(); ++i)
|
|
{
|
|
cmd.push_back(*i);
|
|
}
|
|
return cmSystemTools::RunSingleCommand(cmd, output, retVal, dir,
|
|
outputflag, timeout);
|
|
}
|
|
|
|
bool cmSystemTools::RunSingleCommand(std::vector<std::string>const& command,
|
|
std::string* output ,
|
|
int* retVal , const char* dir ,
|
|
OutputOption outputflag ,
|
|
double timeout )
|
|
{
|
|
std::vector<const char*> argv;
|
|
for(std::vector<std::string>::const_iterator a = command.begin();
|
|
a != command.end(); ++a)
|
|
{
|
|
argv.push_back(a->c_str());
|
|
}
|
|
argv.push_back(0);
|
|
if ( output )
|
|
{
|
|
*output = "";
|
|
}
|
|
|
|
cmsysProcess* cp = cmsysProcess_New();
|
|
cmsysProcess_SetCommand(cp, &*argv.begin());
|
|
cmsysProcess_SetWorkingDirectory(cp, dir);
|
|
if(cmSystemTools::GetRunCommandHideConsole())
|
|
{
|
|
cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
|
|
}
|
|
|
|
if (outputflag == OUTPUT_PASSTHROUGH)
|
|
{
|
|
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
|
|
cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
|
|
}
|
|
|
|
cmsysProcess_SetTimeout(cp, timeout);
|
|
cmsysProcess_Execute(cp);
|
|
|
|
std::vector<char> tempOutput;
|
|
char* data;
|
|
int length;
|
|
int pipe;
|
|
if(outputflag != OUTPUT_PASSTHROUGH && (output || outputflag != OUTPUT_NONE))
|
|
{
|
|
while((pipe = cmsysProcess_WaitForData(cp, &data, &length, 0)) > 0)
|
|
{
|
|
if(output || outputflag != OUTPUT_NONE)
|
|
{
|
|
// Translate NULL characters in the output into valid text.
|
|
// Visual Studio 7 puts these characters in the output of its
|
|
// build process.
|
|
for(int i=0; i < length; ++i)
|
|
{
|
|
if(data[i] == '\0')
|
|
{
|
|
data[i] = ' ';
|
|
}
|
|
}
|
|
}
|
|
if ( output )
|
|
{
|
|
tempOutput.insert(tempOutput.end(), data, data+length);
|
|
}
|
|
if(outputflag != OUTPUT_NONE)
|
|
{
|
|
if(outputflag == OUTPUT_MERGE)
|
|
{
|
|
cmSystemTools::Stdout(data, length);
|
|
}
|
|
else
|
|
{
|
|
if(pipe == cmsysProcess_Pipe_STDERR)
|
|
{
|
|
cmSystemTools::Stderr(data, length);
|
|
}
|
|
else if(pipe == cmsysProcess_Pipe_STDOUT)
|
|
{
|
|
cmSystemTools::Stdout(data, length);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cmsysProcess_WaitForExit(cp, 0);
|
|
if ( output && tempOutput.begin() != tempOutput.end())
|
|
{
|
|
output->append(&*tempOutput.begin(), tempOutput.size());
|
|
}
|
|
|
|
bool result = true;
|
|
if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exited)
|
|
{
|
|
if ( retVal )
|
|
{
|
|
*retVal = cmsysProcess_GetExitValue(cp);
|
|
}
|
|
else
|
|
{
|
|
if ( cmsysProcess_GetExitValue(cp) != 0 )
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Exception)
|
|
{
|
|
const char* exception_str = cmsysProcess_GetExceptionString(cp);
|
|
if ( outputflag != OUTPUT_NONE )
|
|
{
|
|
std::cerr << exception_str << std::endl;
|
|
}
|
|
if ( output )
|
|
{
|
|
output->append(exception_str, strlen(exception_str));
|
|
}
|
|
result = false;
|
|
}
|
|
else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Error)
|
|
{
|
|
const char* error_str = cmsysProcess_GetErrorString(cp);
|
|
if ( outputflag != OUTPUT_NONE )
|
|
{
|
|
std::cerr << error_str << std::endl;
|
|
}
|
|
if ( output )
|
|
{
|
|
output->append(error_str, strlen(error_str));
|
|
}
|
|
result = false;
|
|
}
|
|
else if(cmsysProcess_GetState(cp) == cmsysProcess_State_Expired)
|
|
{
|
|
const char* error_str = "Process terminated due to timeout\n";
|
|
if ( outputflag != OUTPUT_NONE )
|
|
{
|
|
std::cerr << error_str << std::endl;
|
|
}
|
|
if ( output )
|
|
{
|
|
output->append(error_str, strlen(error_str));
|
|
}
|
|
result = false;
|
|
}
|
|
|
|
cmsysProcess_Delete(cp);
|
|
return result;
|
|
}
|
|
|
|
bool cmSystemTools::RunSingleCommand(
|
|
const char* command,
|
|
std::string* output,
|
|
int *retVal,
|
|
const char* dir,
|
|
OutputOption outputflag,
|
|
double timeout)
|
|
{
|
|
if(s_DisableRunCommandOutput)
|
|
{
|
|
outputflag = OUTPUT_NONE;
|
|
}
|
|
|
|
std::vector<cmStdString> args = cmSystemTools::ParseArguments(command);
|
|
|
|
if(args.size() < 1)
|
|
{
|
|
return false;
|
|
}
|
|
return cmSystemTools::RunSingleCommand(args, output,retVal,
|
|
dir, outputflag, timeout);
|
|
}
|
|
|
|
std::string
|
|
cmSystemTools::PrintSingleCommand(std::vector<std::string> const& command)
|
|
{
|
|
std::string commandStr;
|
|
const char* sep = "";
|
|
for(std::vector<std::string>::const_iterator i = command.begin();
|
|
i != command.end(); ++i)
|
|
{
|
|
commandStr += sep;
|
|
commandStr += "\"";
|
|
commandStr += *i;
|
|
commandStr += "\"";
|
|
sep = " ";
|
|
}
|
|
return commandStr;
|
|
}
|
|
|
|
bool cmSystemTools::DoesFileExistWithExtensions(
|
|
const char* name,
|
|
const std::vector<std::string>& headerExts)
|
|
{
|
|
std::string hname;
|
|
|
|
for( std::vector<std::string>::const_iterator ext = headerExts.begin();
|
|
ext != headerExts.end(); ++ext )
|
|
{
|
|
hname = name;
|
|
hname += ".";
|
|
hname += *ext;
|
|
if(cmSystemTools::FileExists(hname.c_str()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string cmSystemTools::FileExistsInParentDirectories(const char* fname,
|
|
const char* directory, const char* toplevel)
|
|
{
|
|
std::string file = fname;
|
|
cmSystemTools::ConvertToUnixSlashes(file);
|
|
std::string dir = directory;
|
|
cmSystemTools::ConvertToUnixSlashes(dir);
|
|
std::string prevDir;
|
|
while(dir != prevDir)
|
|
{
|
|
std::string path = dir + "/" + file;
|
|
if ( cmSystemTools::FileExists(path.c_str()) )
|
|
{
|
|
return path;
|
|
}
|
|
if ( dir.size() < strlen(toplevel) )
|
|
{
|
|
break;
|
|
}
|
|
prevDir = dir;
|
|
dir = cmSystemTools::GetParentDirectory(dir.c_str());
|
|
}
|
|
return "";
|
|
}
|
|
|
|
bool cmSystemTools::cmCopyFile(const char* source, const char* destination)
|
|
{
|
|
return Superclass::CopyFileAlways(source, destination);
|
|
}
|
|
|
|
bool cmSystemTools::CopyFileIfDifferent(const char* source,
|
|
const char* destination)
|
|
{
|
|
return Superclass::CopyFileIfDifferent(source, destination);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#ifdef _WIN32
|
|
cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsFileRetry()
|
|
{
|
|
static WindowsFileRetry retry = {0,0};
|
|
if(!retry.Count)
|
|
{
|
|
unsigned int data[2] = {0,0};
|
|
HKEY const keys[2] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
|
|
wchar_t const* const values[2] = {L"FilesystemRetryCount",
|
|
L"FilesystemRetryDelay"};
|
|
for(int k=0; k < 2; ++k)
|
|
{
|
|
HKEY hKey;
|
|
if(RegOpenKeyExW(keys[k], L"Software\\Kitware\\CMake\\Config",
|
|
0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
for(int v=0; v < 2; ++v)
|
|
{
|
|
DWORD dwData, dwType, dwSize = 4;
|
|
if(!data[v] &&
|
|
RegQueryValueExW(hKey, values[v], 0, &dwType, (BYTE *)&dwData,
|
|
&dwSize) == ERROR_SUCCESS &&
|
|
dwType == REG_DWORD && dwSize == 4)
|
|
{
|
|
data[v] = static_cast<unsigned int>(dwData);
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
retry.Count = data[0]? data[0] : 5;
|
|
retry.Delay = data[1]? data[1] : 500;
|
|
}
|
|
return retry;
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::RenameFile(const char* oldname, const char* newname)
|
|
{
|
|
#ifdef _WIN32
|
|
# ifndef INVALID_FILE_ATTRIBUTES
|
|
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
|
|
# endif
|
|
/* Windows MoveFileEx may not replace read-only or in-use files. If it
|
|
fails then remove the read-only attribute from any existing destination.
|
|
Try multiple times since we may be racing against another process
|
|
creating/opening the destination file just before our MoveFileEx. */
|
|
WindowsFileRetry retry = cmSystemTools::GetWindowsFileRetry();
|
|
while(!MoveFileExW(cmsys::Encoding::ToWide(oldname).c_str(),
|
|
cmsys::Encoding::ToWide(newname).c_str(),
|
|
MOVEFILE_REPLACE_EXISTING) && --retry.Count)
|
|
{
|
|
// Try again only if failure was due to access permissions.
|
|
if(GetLastError() != ERROR_ACCESS_DENIED)
|
|
{
|
|
return false;
|
|
}
|
|
DWORD attrs =
|
|
GetFileAttributesW(cmsys::Encoding::ToWide(newname).c_str());
|
|
if((attrs != INVALID_FILE_ATTRIBUTES) &&
|
|
(attrs & FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
// Remove the read-only attribute from the destination file.
|
|
SetFileAttributesW(cmsys::Encoding::ToWide(newname).c_str(),
|
|
attrs & ~FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
else
|
|
{
|
|
// The file may be temporarily in use so wait a bit.
|
|
cmSystemTools::Delay(retry.Delay);
|
|
}
|
|
}
|
|
return retry.Count > 0;
|
|
#else
|
|
/* On UNIX we have an OS-provided call to do this atomically. */
|
|
return rename(oldname, newname) == 0;
|
|
#endif
|
|
}
|
|
|
|
bool cmSystemTools::ComputeFileMD5(const char* source, char* md5out)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
cmCryptoHashMD5 md5;
|
|
std::string str = md5.HashFile(source);
|
|
strncpy(md5out, str.c_str(), 32);
|
|
return !str.empty();
|
|
#else
|
|
(void)source;
|
|
(void)md5out;
|
|
cmSystemTools::Message("md5sum not supported in bootstrapping mode","Error");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
std::string cmSystemTools::ComputeStringMD5(const char* input)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
cmCryptoHashMD5 md5;
|
|
return md5.HashString(input);
|
|
#else
|
|
(void)input;
|
|
cmSystemTools::Message("md5sum not supported in bootstrapping mode","Error");
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
void cmSystemTools::Glob(const char *directory, const char *regexp,
|
|
std::vector<std::string>& files)
|
|
{
|
|
cmsys::Directory d;
|
|
cmsys::RegularExpression reg(regexp);
|
|
|
|
if (d.Load(directory))
|
|
{
|
|
size_t numf;
|
|
unsigned int i;
|
|
numf = d.GetNumberOfFiles();
|
|
for (i = 0; i < numf; i++)
|
|
{
|
|
std::string fname = d.GetFile(i);
|
|
if (reg.find(fname))
|
|
{
|
|
files.push_back(fname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cmSystemTools::GlobDirs(const char *fullPath,
|
|
std::vector<std::string>& files)
|
|
{
|
|
std::string path = fullPath;
|
|
std::string::size_type pos = path.find("/*");
|
|
if(pos == std::string::npos)
|
|
{
|
|
files.push_back(fullPath);
|
|
return;
|
|
}
|
|
std::string startPath = path.substr(0, pos);
|
|
std::string finishPath = path.substr(pos+2);
|
|
|
|
cmsys::Directory d;
|
|
if (d.Load(startPath.c_str()))
|
|
{
|
|
for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i)
|
|
{
|
|
if((std::string(d.GetFile(i)) != ".")
|
|
&& (std::string(d.GetFile(i)) != ".."))
|
|
{
|
|
std::string fname = startPath;
|
|
fname +="/";
|
|
fname += d.GetFile(i);
|
|
if(cmSystemTools::FileIsDirectory(fname.c_str()))
|
|
{
|
|
fname += finishPath;
|
|
cmSystemTools::GlobDirs(fname.c_str(), files);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void cmSystemTools::ExpandList(std::vector<std::string> const& arguments,
|
|
std::vector<std::string>& newargs)
|
|
{
|
|
std::vector<std::string>::const_iterator i;
|
|
for(i = arguments.begin();i != arguments.end(); ++i)
|
|
{
|
|
cmSystemTools::ExpandListArgument(*i, newargs);
|
|
}
|
|
}
|
|
|
|
void cmSystemTools::ExpandListArgument(const std::string& arg,
|
|
std::vector<std::string>& newargs,
|
|
bool emptyArgs)
|
|
{
|
|
// If argument is empty, it is an empty list.
|
|
if(arg.length() == 0 && !emptyArgs)
|
|
{
|
|
return;
|
|
}
|
|
// if there are no ; in the name then just copy the current string
|
|
if(arg.find(';') == std::string::npos)
|
|
{
|
|
newargs.push_back(arg);
|
|
return;
|
|
}
|
|
std::vector<char> newArgVec;
|
|
// Break the string at non-escaped semicolons not nested in [].
|
|
int squareNesting = 0;
|
|
for(const char* c = arg.c_str(); *c; ++c)
|
|
{
|
|
switch(*c)
|
|
{
|
|
case '\\':
|
|
{
|
|
// We only want to allow escaping of semicolons. Other
|
|
// escapes should not be processed here.
|
|
++c;
|
|
if(*c == ';')
|
|
{
|
|
newArgVec.push_back(*c);
|
|
}
|
|
else
|
|
{
|
|
newArgVec.push_back('\\');
|
|
if(*c)
|
|
{
|
|
newArgVec.push_back(*c);
|
|
}
|
|
else
|
|
{
|
|
// Terminate the loop properly.
|
|
--c;
|
|
}
|
|
}
|
|
} break;
|
|
case '[':
|
|
{
|
|
++squareNesting;
|
|
newArgVec.push_back(*c);
|
|
} break;
|
|
case ']':
|
|
{
|
|
--squareNesting;
|
|
newArgVec.push_back(*c);
|
|
} break;
|
|
case ';':
|
|
{
|
|
// Break the string here if we are not nested inside square
|
|
// brackets.
|
|
if(squareNesting == 0)
|
|
{
|
|
if ( newArgVec.size() || emptyArgs )
|
|
{
|
|
// Add the last argument if the string is not empty.
|
|
newArgVec.push_back(0);
|
|
newargs.push_back(&*newArgVec.begin());
|
|
newArgVec.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newArgVec.push_back(*c);
|
|
}
|
|
} break;
|
|
default:
|
|
{
|
|
// Just append this character.
|
|
newArgVec.push_back(*c);
|
|
} break;
|
|
}
|
|
}
|
|
if ( newArgVec.size() || emptyArgs )
|
|
{
|
|
// Add the last argument if the string is not empty.
|
|
newArgVec.push_back(0);
|
|
newargs.push_back(&*newArgVec.begin());
|
|
}
|
|
}
|
|
|
|
bool cmSystemTools::SimpleGlob(const cmStdString& glob,
|
|
std::vector<cmStdString>& files,
|
|
int type /* = 0 */)
|
|
{
|
|
files.clear();
|
|
if ( glob[glob.size()-1] != '*' )
|
|
{
|
|
return false;
|
|
}
|
|
std::string path = cmSystemTools::GetFilenamePath(glob);
|
|
std::string ppath = cmSystemTools::GetFilenameName(glob);
|
|
ppath = ppath.substr(0, ppath.size()-1);
|
|
if ( path.size() == 0 )
|
|
{
|
|
path = "/";
|
|
}
|
|
|
|
bool res = false;
|
|
cmsys::Directory d;
|
|
if (d.Load(path.c_str()))
|
|
{
|
|
for (unsigned int i = 0; i < d.GetNumberOfFiles(); ++i)
|
|
{
|
|
if((std::string(d.GetFile(i)) != ".")
|
|
&& (std::string(d.GetFile(i)) != ".."))
|
|
{
|
|
std::string fname = path;
|
|
if ( path[path.size()-1] != '/' )
|
|
{
|
|
fname +="/";
|
|
}
|
|
fname += d.GetFile(i);
|
|
std::string sfname = d.GetFile(i);
|
|
if ( type > 0 && cmSystemTools::FileIsDirectory(fname.c_str()) )
|
|
{
|
|
continue;
|
|
}
|
|
if ( type < 0 && !cmSystemTools::FileIsDirectory(fname.c_str()) )
|
|
{
|
|
continue;
|
|
}
|
|
if ( sfname.size() >= ppath.size() &&
|
|
sfname.substr(0, ppath.size()) ==
|
|
ppath )
|
|
{
|
|
files.push_back(fname);
|
|
res = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
cmSystemTools::FileFormat cmSystemTools::GetFileFormat(const char* cext)
|
|
{
|
|
if ( ! cext || *cext == 0 )
|
|
{
|
|
return cmSystemTools::NO_FILE_FORMAT;
|
|
}
|
|
//std::string ext = cmSystemTools::LowerCase(cext);
|
|
std::string ext = cext;
|
|
if ( ext == "c" || ext == ".c" ||
|
|
ext == "m" || ext == ".m"
|
|
) { return cmSystemTools::C_FILE_FORMAT; }
|
|
if (
|
|
ext == "C" || ext == ".C" ||
|
|
ext == "M" || ext == ".M" ||
|
|
ext == "c++" || ext == ".c++" ||
|
|
ext == "cc" || ext == ".cc" ||
|
|
ext == "cpp" || ext == ".cpp" ||
|
|
ext == "cxx" || ext == ".cxx" ||
|
|
ext == "mm" || ext == ".mm"
|
|
) { return cmSystemTools::CXX_FILE_FORMAT; }
|
|
if (
|
|
ext == "f" || ext == ".f" ||
|
|
ext == "F" || ext == ".F" ||
|
|
ext == "f77" || ext == ".f77" ||
|
|
ext == "f90" || ext == ".f90" ||
|
|
ext == "for" || ext == ".for" ||
|
|
ext == "f95" || ext == ".f95"
|
|
) { return cmSystemTools::FORTRAN_FILE_FORMAT; }
|
|
if ( ext == "java" || ext == ".java" )
|
|
{ return cmSystemTools::JAVA_FILE_FORMAT; }
|
|
if (
|
|
ext == "H" || ext == ".H" ||
|
|
ext == "h" || ext == ".h" ||
|
|
ext == "h++" || ext == ".h++" ||
|
|
ext == "hm" || ext == ".hm" ||
|
|
ext == "hpp" || ext == ".hpp" ||
|
|
ext == "hxx" || ext == ".hxx" ||
|
|
ext == "in" || ext == ".in" ||
|
|
ext == "txx" || ext == ".txx"
|
|
) { return cmSystemTools::HEADER_FILE_FORMAT; }
|
|
if ( ext == "rc" || ext == ".rc" )
|
|
{ return cmSystemTools::RESOURCE_FILE_FORMAT; }
|
|
if ( ext == "def" || ext == ".def" )
|
|
{ return cmSystemTools::DEFINITION_FILE_FORMAT; }
|
|
if ( ext == "lib" || ext == ".lib" ||
|
|
ext == "a" || ext == ".a")
|
|
{ return cmSystemTools::STATIC_LIBRARY_FILE_FORMAT; }
|
|
if ( ext == "o" || ext == ".o" ||
|
|
ext == "obj" || ext == ".obj")
|
|
{ return cmSystemTools::OBJECT_FILE_FORMAT; }
|
|
#ifdef __APPLE__
|
|
if ( ext == "dylib" || ext == ".dylib" )
|
|
{ return cmSystemTools::SHARED_LIBRARY_FILE_FORMAT; }
|
|
if ( ext == "so" || ext == ".so" ||
|
|
ext == "bundle" || ext == ".bundle" )
|
|
{ return cmSystemTools::MODULE_FILE_FORMAT; }
|
|
#else // __APPLE__
|
|
if ( ext == "so" || ext == ".so" ||
|
|
ext == "sl" || ext == ".sl" ||
|
|
ext == "dll" || ext == ".dll" )
|
|
{ return cmSystemTools::SHARED_LIBRARY_FILE_FORMAT; }
|
|
#endif // __APPLE__
|
|
return cmSystemTools::UNKNOWN_FILE_FORMAT;
|
|
}
|
|
|
|
bool cmSystemTools::Split(const char* s, std::vector<cmStdString>& l)
|
|
{
|
|
std::vector<std::string> temp;
|
|
bool res = Superclass::Split(s, temp);
|
|
for(std::vector<std::string>::const_iterator i = temp.begin();
|
|
i != temp.end(); ++i)
|
|
{
|
|
l.push_back(*i);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
std::string cmSystemTools::ConvertToOutputPath(const char* path)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if(s_ForceUnixPaths)
|
|
{
|
|
return cmSystemTools::ConvertToUnixOutputPath(path);
|
|
}
|
|
return cmSystemTools::ConvertToWindowsOutputPath(path);
|
|
#else
|
|
return cmSystemTools::ConvertToUnixOutputPath(path);
|
|
#endif
|
|
}
|
|
|
|
void cmSystemTools::ConvertToOutputSlashes(std::string& path)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
if(!s_ForceUnixPaths)
|
|
{
|
|
// Convert to windows slashes.
|
|
std::string::size_type pos = 0;
|
|
while((pos = path.find('/', pos)) != std::string::npos)
|
|
{
|
|
path[pos++] = '\\';
|
|
}
|
|
}
|
|
#else
|
|
static_cast<void>(path);
|
|
#endif
|
|
}
|
|
|
|
std::string cmSystemTools::ConvertToRunCommandPath(const char* path)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
return cmSystemTools::ConvertToWindowsOutputPath(path);
|
|
#else
|
|
return cmSystemTools::ConvertToUnixOutputPath(path);
|
|
#endif
|
|
}
|
|
|
|
bool cmSystemTools::StringEndsWith(const char* str1, const char* str2)
|
|
{
|
|
if ( !str1 || !str2 || strlen(str1) < strlen(str2) )
|
|
{
|
|
return 0;
|
|
}
|
|
return !strncmp(str1 + (strlen(str1)-strlen(str2)), str2, strlen(str2));
|
|
}
|
|
|
|
// compute the relative path from here to there
|
|
std::string cmSystemTools::RelativePath(const char* local, const char* remote)
|
|
{
|
|
if(!cmSystemTools::FileIsFullPath(local))
|
|
{
|
|
cmSystemTools::Error("RelativePath must be passed a full path to local: ",
|
|
local);
|
|
}
|
|
if(!cmSystemTools::FileIsFullPath(remote))
|
|
{
|
|
cmSystemTools::Error
|
|
("RelativePath must be passed a full path to remote: ", remote);
|
|
}
|
|
return cmsys::SystemTools::RelativePath(local, remote);
|
|
}
|
|
|
|
std::string cmSystemTools::CollapseCombinedPath(std::string const& dir,
|
|
std::string const& file)
|
|
{
|
|
if(dir.empty() || dir == ".")
|
|
{
|
|
return file;
|
|
}
|
|
|
|
std::vector<std::string> dirComponents;
|
|
std::vector<std::string> fileComponents;
|
|
cmSystemTools::SplitPath(dir.c_str(), dirComponents);
|
|
cmSystemTools::SplitPath(file.c_str(), fileComponents);
|
|
|
|
if(fileComponents.empty())
|
|
{
|
|
return dir;
|
|
}
|
|
if(fileComponents[0] != "")
|
|
{
|
|
// File is not a relative path.
|
|
return file;
|
|
}
|
|
|
|
std::vector<std::string>::iterator i = fileComponents.begin()+1;
|
|
while(i != fileComponents.end() && *i == ".." && dirComponents.size() > 1)
|
|
{
|
|
++i; // Remove ".." file component.
|
|
dirComponents.pop_back(); // Remove last dir component.
|
|
}
|
|
|
|
dirComponents.insert(dirComponents.end(), i, fileComponents.end());
|
|
return cmSystemTools::JoinPath(dirComponents);
|
|
}
|
|
|
|
#ifdef CMAKE_BUILD_WITH_CMAKE
|
|
//----------------------------------------------------------------------
|
|
bool cmSystemTools::UnsetEnv(const char* value)
|
|
{
|
|
#if !defined(HAVE_UNSETENV)
|
|
std::string var = value;
|
|
var += "=";
|
|
return cmSystemTools::PutEnv(var.c_str());
|
|
#else
|
|
unsetenv(value);
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
|
|
{
|
|
std::vector<std::string> env;
|
|
int cc;
|
|
for ( cc = 0; environ[cc]; ++ cc )
|
|
{
|
|
env.push_back(environ[cc]);
|
|
}
|
|
return env;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void cmSystemTools::AppendEnv(std::vector<std::string> const& env)
|
|
{
|
|
for(std::vector<std::string>::const_iterator eit = env.begin();
|
|
eit != env.end(); ++eit)
|
|
{
|
|
cmSystemTools::PutEnv(eit->c_str());
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment()
|
|
{
|
|
this->Env = cmSystemTools::GetEnvironmentVariables();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
cmSystemTools::SaveRestoreEnvironment::~SaveRestoreEnvironment()
|
|
{
|
|
// First clear everything in the current environment:
|
|
std::vector<std::string> currentEnv = GetEnvironmentVariables();
|
|
for(std::vector<std::string>::const_iterator
|
|
eit = currentEnv.begin(); eit != currentEnv.end(); ++eit)
|
|
{
|
|
std::string var(*eit);
|
|
|
|
std::string::size_type pos = var.find("=");
|
|
if (pos != std::string::npos)
|
|
{
|
|
var = var.substr(0, pos);
|
|
}
|
|
|
|
cmSystemTools::UnsetEnv(var.c_str());
|
|
}
|
|
|
|
// Then put back each entry from the original environment:
|
|
cmSystemTools::AppendEnv(this->Env);
|
|
}
|
|
#endif
|
|
|
|
void cmSystemTools::EnableVSConsoleOutput()
|
|
{
|
|
#ifdef _WIN32
|
|
// Visual Studio 8 2005 (devenv.exe or VCExpress.exe) will not
|
|
// display output to the console unless this environment variable is
|
|
// set. We need it to capture the output of these build tools.
|
|
// Note for future work that one could pass "/out \\.\pipe\NAME" to
|
|
// either of these executables where NAME is created with
|
|
// CreateNamedPipe. This would bypass the internal buffering of the
|
|
// output and allow it to be captured on the fly.
|
|
cmSystemTools::PutEnv("vsconsoleoutput=1");
|
|
|
|
# ifdef CMAKE_BUILD_WITH_CMAKE
|
|
// VS sets an environment variable to tell MS tools like "cl" to report
|
|
// output through a backdoor pipe instead of stdout/stderr. Unset the
|
|
// environment variable to close this backdoor for any path of process
|
|
// invocations that passes through CMake so we can capture the output.
|
|
cmSystemTools::UnsetEnv("VS_UNICODE_OUTPUT");
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
bool cmSystemTools::IsPathToFramework(const char* path)
|
|
{
|
|
if(cmSystemTools::FileIsFullPath(path))
|
|
{
|
|
std::string libname = path;
|
|
if(libname.find(".framework") == libname.size()+1-sizeof(".framework"))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cmSystemTools::CreateTar(const char* outFileName,
|
|
const std::vector<cmStdString>& files,
|
|
bool gzip, bool bzip2, bool verbose)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
|
|
cmsys::ofstream fout(outFileName, std::ios::out | cmsys_ios_binary);
|
|
if(!fout)
|
|
{
|
|
std::string e = "Cannot open output file \"";
|
|
e += outFileName;
|
|
e += "\": ";
|
|
e += cmSystemTools::GetLastSystemError();
|
|
cmSystemTools::Error(e.c_str());
|
|
return false;
|
|
}
|
|
cmArchiveWrite a(fout, (gzip? cmArchiveWrite::CompressGZip :
|
|
(bzip2? cmArchiveWrite::CompressBZip2 :
|
|
cmArchiveWrite::CompressNone)),
|
|
cmArchiveWrite::TypeTAR);
|
|
a.SetVerbose(verbose);
|
|
for(std::vector<cmStdString>::const_iterator i = files.begin();
|
|
i != files.end(); ++i)
|
|
{
|
|
std::string path = *i;
|
|
if(cmSystemTools::FileIsFullPath(path.c_str()))
|
|
{
|
|
// Get the relative path to the file.
|
|
path = cmSystemTools::RelativePath(cwd.c_str(), path.c_str());
|
|
}
|
|
if(!a.Add(path))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if(!a)
|
|
{
|
|
cmSystemTools::Error(a.GetError().c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
#else
|
|
(void)outFileName;
|
|
(void)files;
|
|
(void)gzip;
|
|
(void)verbose;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
namespace{
|
|
#define BSDTAR_FILESIZE_PRINTF "%lu"
|
|
#define BSDTAR_FILESIZE_TYPE unsigned long
|
|
void
|
|
list_item_verbose(FILE *out, struct archive_entry *entry)
|
|
{
|
|
char tmp[100];
|
|
size_t w;
|
|
const char *p;
|
|
const char *fmt;
|
|
time_t tim;
|
|
static time_t now;
|
|
size_t u_width = 6;
|
|
size_t gs_width = 13;
|
|
|
|
/*
|
|
* We avoid collecting the entire list in memory at once by
|
|
* listing things as we see them. However, that also means we can't
|
|
* just pre-compute the field widths. Instead, we start with guesses
|
|
* and just widen them as necessary. These numbers are completely
|
|
* arbitrary.
|
|
*/
|
|
if (!now)
|
|
{
|
|
time(&now);
|
|
}
|
|
fprintf(out, "%s %d ",
|
|
archive_entry_strmode(entry),
|
|
archive_entry_nlink(entry));
|
|
|
|
/* Use uname if it's present, else uid. */
|
|
p = archive_entry_uname(entry);
|
|
if ((p == NULL) || (*p == '\0'))
|
|
{
|
|
sprintf(tmp, "%lu ",
|
|
(unsigned long)archive_entry_uid(entry));
|
|
p = tmp;
|
|
}
|
|
w = strlen(p);
|
|
if (w > u_width)
|
|
{
|
|
u_width = w;
|
|
}
|
|
fprintf(out, "%-*s ", (int)u_width, p);
|
|
/* Use gname if it's present, else gid. */
|
|
p = archive_entry_gname(entry);
|
|
if (p != NULL && p[0] != '\0')
|
|
{
|
|
fprintf(out, "%s", p);
|
|
w = strlen(p);
|
|
}
|
|
else
|
|
{
|
|
sprintf(tmp, "%lu",
|
|
(unsigned long)archive_entry_gid(entry));
|
|
w = strlen(tmp);
|
|
fprintf(out, "%s", tmp);
|
|
}
|
|
|
|
/*
|
|
* Print device number or file size, right-aligned so as to make
|
|
* total width of group and devnum/filesize fields be gs_width.
|
|
* If gs_width is too small, grow it.
|
|
*/
|
|
if (archive_entry_filetype(entry) == AE_IFCHR
|
|
|| archive_entry_filetype(entry) == AE_IFBLK)
|
|
{
|
|
sprintf(tmp, "%lu,%lu",
|
|
(unsigned long)archive_entry_rdevmajor(entry),
|
|
(unsigned long)archive_entry_rdevminor(entry));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Note the use of platform-dependent macros to format
|
|
* the filesize here. We need the format string and the
|
|
* corresponding type for the cast.
|
|
*/
|
|
sprintf(tmp, BSDTAR_FILESIZE_PRINTF,
|
|
(BSDTAR_FILESIZE_TYPE)archive_entry_size(entry));
|
|
}
|
|
if (w + strlen(tmp) >= gs_width)
|
|
{
|
|
gs_width = w+strlen(tmp)+1;
|
|
}
|
|
fprintf(out, "%*s", (int)(gs_width - w), tmp);
|
|
|
|
/* Format the time using 'ls -l' conventions. */
|
|
tim = archive_entry_mtime(entry);
|
|
#define HALF_YEAR (time_t)365 * 86400 / 2
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
/* Windows' strftime function does not support %e format. */
|
|
#define DAY_FMT "%d"
|
|
#else
|
|
#define DAY_FMT "%e" /* Day number without leading zeros */
|
|
#endif
|
|
if (tim < now - HALF_YEAR || tim > now + HALF_YEAR)
|
|
{
|
|
fmt = DAY_FMT " %b %Y";
|
|
}
|
|
else
|
|
{
|
|
fmt = DAY_FMT " %b %H:%M";
|
|
}
|
|
strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
|
|
fprintf(out, " %s ", tmp);
|
|
fprintf(out, "%s", archive_entry_pathname(entry));
|
|
|
|
/* Extra information for links. */
|
|
if (archive_entry_hardlink(entry)) /* Hard link */
|
|
{
|
|
fprintf(out, " link to %s",
|
|
archive_entry_hardlink(entry));
|
|
}
|
|
else if (archive_entry_symlink(entry)) /* Symbolic link */
|
|
{
|
|
fprintf(out, " -> %s", archive_entry_symlink(entry));
|
|
}
|
|
}
|
|
#ifdef __BORLANDC__
|
|
# pragma warn -8066 /* unreachable code */
|
|
#endif
|
|
|
|
long copy_data(struct archive *ar, struct archive *aw)
|
|
{
|
|
long r;
|
|
const void *buff;
|
|
size_t size;
|
|
#if defined(ARCHIVE_VERSION_NUMBER) && ARCHIVE_VERSION_NUMBER >= 3000000
|
|
__LA_INT64_T offset;
|
|
#else
|
|
off_t offset;
|
|
#endif
|
|
|
|
for (;;)
|
|
{
|
|
r = archive_read_data_block(ar, &buff, &size, &offset);
|
|
if (r == ARCHIVE_EOF)
|
|
{
|
|
return (ARCHIVE_OK);
|
|
}
|
|
if (r != ARCHIVE_OK)
|
|
{
|
|
return (r);
|
|
}
|
|
r = archive_write_data_block(aw, buff, size, offset);
|
|
if (r != ARCHIVE_OK)
|
|
{
|
|
cmSystemTools::Message("archive_write_data_block()",
|
|
archive_error_string(aw));
|
|
return (r);
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
bool extract_tar(const char* outFileName, bool verbose,
|
|
bool extract)
|
|
{
|
|
struct archive* a = archive_read_new();
|
|
struct archive *ext = archive_write_disk_new();
|
|
archive_read_support_compression_all(a);
|
|
archive_read_support_format_all(a);
|
|
struct archive_entry *entry;
|
|
int r = archive_read_open_file(a, outFileName, 10240);
|
|
if(r)
|
|
{
|
|
cmSystemTools::Error("Problem with archive_read_open_file(): ",
|
|
archive_error_string(a));
|
|
return false;
|
|
}
|
|
for (;;)
|
|
{
|
|
r = archive_read_next_header(a, &entry);
|
|
if (r == ARCHIVE_EOF)
|
|
{
|
|
break;
|
|
}
|
|
if (r != ARCHIVE_OK)
|
|
{
|
|
cmSystemTools::Error("Problem with archive_read_next_header(): ",
|
|
archive_error_string(a));
|
|
break;
|
|
}
|
|
if(verbose)
|
|
{
|
|
if(extract)
|
|
{
|
|
cmSystemTools::Stdout("x ");
|
|
cmSystemTools::Stdout(archive_entry_pathname(entry));
|
|
}
|
|
else
|
|
{
|
|
list_item_verbose(stdout, entry);
|
|
}
|
|
cmSystemTools::Stdout("\n");
|
|
}
|
|
else if(!extract)
|
|
{
|
|
cmSystemTools::Stdout(archive_entry_pathname(entry));
|
|
cmSystemTools::Stdout("\n");
|
|
}
|
|
if(extract)
|
|
{
|
|
r = archive_write_disk_set_options(ext, ARCHIVE_EXTRACT_TIME);
|
|
if (r != ARCHIVE_OK)
|
|
{
|
|
cmSystemTools::Error(
|
|
"Problem with archive_write_disk_set_options(): ",
|
|
archive_error_string(ext));
|
|
break;
|
|
}
|
|
|
|
r = archive_write_header(ext, entry);
|
|
if (r == ARCHIVE_OK)
|
|
{
|
|
copy_data(a, ext);
|
|
r = archive_write_finish_entry(ext);
|
|
if (r != ARCHIVE_OK)
|
|
{
|
|
cmSystemTools::Error("Problem with archive_write_finish_entry(): ",
|
|
archive_error_string(ext));
|
|
break;
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
else if(const char* linktext = archive_entry_symlink(entry))
|
|
{
|
|
std::cerr << "cmake -E tar: warning: skipping symbolic link \""
|
|
<< archive_entry_pathname(entry) << "\" -> \""
|
|
<< linktext << "\"." << std::endl;
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
cmSystemTools::Error("Problem with archive_write_header(): ",
|
|
archive_error_string(ext));
|
|
cmSystemTools::Error("Current file: ",
|
|
archive_entry_pathname(entry));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
archive_read_close(a);
|
|
archive_read_finish(a);
|
|
return r == ARCHIVE_EOF || r == ARCHIVE_OK;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool cmSystemTools::ExtractTar(const char* outFileName,
|
|
bool , bool verbose)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
return extract_tar(outFileName, verbose, true);
|
|
#else
|
|
(void)outFileName;
|
|
(void)verbose;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool cmSystemTools::ListTar(const char* outFileName,
|
|
bool ,
|
|
bool verbose)
|
|
{
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
return extract_tar(outFileName, verbose, false);
|
|
#else
|
|
(void)outFileName;
|
|
(void)verbose;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
|
|
double timeout,
|
|
std::vector<char>& out,
|
|
std::vector<char>& err)
|
|
{
|
|
line = "";
|
|
std::vector<char>::iterator outiter = out.begin();
|
|
std::vector<char>::iterator erriter = err.begin();
|
|
while(1)
|
|
{
|
|
// Check for a newline in stdout.
|
|
for(;outiter != out.end(); ++outiter)
|
|
{
|
|
if((*outiter == '\r') && ((outiter+1) == out.end()))
|
|
{
|
|
break;
|
|
}
|
|
else if(*outiter == '\n' || *outiter == '\0')
|
|
{
|
|
std::vector<char>::size_type length = outiter-out.begin();
|
|
if(length > 1 && *(outiter-1) == '\r')
|
|
{
|
|
--length;
|
|
}
|
|
if(length > 0)
|
|
{
|
|
line.append(&out[0], length);
|
|
}
|
|
out.erase(out.begin(), outiter+1);
|
|
return cmsysProcess_Pipe_STDOUT;
|
|
}
|
|
}
|
|
|
|
// Check for a newline in stderr.
|
|
for(;erriter != err.end(); ++erriter)
|
|
{
|
|
if((*erriter == '\r') && ((erriter+1) == err.end()))
|
|
{
|
|
break;
|
|
}
|
|
else if(*erriter == '\n' || *erriter == '\0')
|
|
{
|
|
std::vector<char>::size_type length = erriter-err.begin();
|
|
if(length > 1 && *(erriter-1) == '\r')
|
|
{
|
|
--length;
|
|
}
|
|
if(length > 0)
|
|
{
|
|
line.append(&err[0], length);
|
|
}
|
|
err.erase(err.begin(), erriter+1);
|
|
return cmsysProcess_Pipe_STDERR;
|
|
}
|
|
}
|
|
|
|
// No newlines found. Wait for more data from the process.
|
|
int length;
|
|
char* data;
|
|
int pipe = cmsysProcess_WaitForData(process, &data, &length, &timeout);
|
|
if(pipe == cmsysProcess_Pipe_Timeout)
|
|
{
|
|
// Timeout has been exceeded.
|
|
return pipe;
|
|
}
|
|
else if(pipe == cmsysProcess_Pipe_STDOUT)
|
|
{
|
|
// Append to the stdout buffer.
|
|
std::vector<char>::size_type size = out.size();
|
|
out.insert(out.end(), data, data+length);
|
|
outiter = out.begin()+size;
|
|
}
|
|
else if(pipe == cmsysProcess_Pipe_STDERR)
|
|
{
|
|
// Append to the stderr buffer.
|
|
std::vector<char>::size_type size = err.size();
|
|
err.insert(err.end(), data, data+length);
|
|
erriter = err.begin()+size;
|
|
}
|
|
else if(pipe == cmsysProcess_Pipe_None)
|
|
{
|
|
// Both stdout and stderr pipes have broken. Return leftover data.
|
|
if(!out.empty())
|
|
{
|
|
line.append(&out[0], outiter-out.begin());
|
|
out.erase(out.begin(), out.end());
|
|
return cmsysProcess_Pipe_STDOUT;
|
|
}
|
|
else if(!err.empty())
|
|
{
|
|
line.append(&err[0], erriter-err.begin());
|
|
err.erase(err.begin(), err.end());
|
|
return cmsysProcess_Pipe_STDERR;
|
|
}
|
|
else
|
|
{
|
|
return cmsysProcess_Pipe_None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmSystemTools::DoNotInheritStdPipes()
|
|
{
|
|
#ifdef _WIN32
|
|
// Check to see if we are attached to a console
|
|
// if so, then do not stop the inherited pipes
|
|
// or stdout and stderr will not show up in dos
|
|
// shell windows
|
|
CONSOLE_SCREEN_BUFFER_INFO hOutInfo;
|
|
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if(GetConsoleScreenBufferInfo(hOut, &hOutInfo))
|
|
{
|
|
return;
|
|
}
|
|
{
|
|
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
DuplicateHandle(GetCurrentProcess(), out,
|
|
GetCurrentProcess(), &out, 0, FALSE,
|
|
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
|
SetStdHandle(STD_OUTPUT_HANDLE, out);
|
|
}
|
|
{
|
|
HANDLE out = GetStdHandle(STD_ERROR_HANDLE);
|
|
DuplicateHandle(GetCurrentProcess(), out,
|
|
GetCurrentProcess(), &out, 0, FALSE,
|
|
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
|
|
SetStdHandle(STD_ERROR_HANDLE, out);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::CopyFileTime(const char* fromFile, const char* toFile)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
cmSystemToolsWindowsHandle hFrom =
|
|
CreateFileW(cmsys::Encoding::ToWide(fromFile).c_str(),
|
|
GENERIC_READ, FILE_SHARE_READ, 0,
|
|
OPEN_EXISTING, 0, 0);
|
|
cmSystemToolsWindowsHandle hTo =
|
|
CreateFileW(cmsys::Encoding::ToWide(toFile).c_str(),
|
|
GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
|
|
if(!hFrom || !hTo)
|
|
{
|
|
return false;
|
|
}
|
|
FILETIME timeCreation;
|
|
FILETIME timeLastAccess;
|
|
FILETIME timeLastWrite;
|
|
if(!GetFileTime(hFrom, &timeCreation, &timeLastAccess, &timeLastWrite))
|
|
{
|
|
return false;
|
|
}
|
|
if(!SetFileTime(hTo, &timeCreation, &timeLastAccess, &timeLastWrite))
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
struct stat fromStat;
|
|
if(stat(fromFile, &fromStat) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
struct utimbuf buf;
|
|
buf.actime = fromStat.st_atime;
|
|
buf.modtime = fromStat.st_mtime;
|
|
if(utime(toFile, &buf) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
cmSystemToolsFileTime* cmSystemTools::FileTimeNew()
|
|
{
|
|
return new cmSystemToolsFileTime;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void cmSystemTools::FileTimeDelete(cmSystemToolsFileTime* t)
|
|
{
|
|
delete t;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::FileTimeGet(const char* fname, cmSystemToolsFileTime* t)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
cmSystemToolsWindowsHandle h =
|
|
CreateFileW(cmsys::Encoding::ToWide(fname).c_str(),
|
|
GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
|
if(!h)
|
|
{
|
|
return false;
|
|
}
|
|
if(!GetFileTime(h, &t->timeCreation, &t->timeLastAccess, &t->timeLastWrite))
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
struct stat st;
|
|
if(stat(fname, &st) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
t->timeBuf.actime = st.st_atime;
|
|
t->timeBuf.modtime = st.st_mtime;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::FileTimeSet(const char* fname, cmSystemToolsFileTime* t)
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
cmSystemToolsWindowsHandle h =
|
|
CreateFileW(cmsys::Encoding::ToWide(fname).c_str(),
|
|
GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
|
|
if(!h)
|
|
{
|
|
return false;
|
|
}
|
|
if(!SetFileTime(h, &t->timeCreation, &t->timeLastAccess, &t->timeLastWrite))
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
if(utime(fname, &t->timeBuf) < 0)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#ifdef _WIN32
|
|
# ifndef CRYPT_SILENT
|
|
# define CRYPT_SILENT 0x40 /* Not defined by VS 6 version of header. */
|
|
# endif
|
|
static int WinCryptRandom(void* data, size_t size)
|
|
{
|
|
int result = 0;
|
|
HCRYPTPROV hProvider = 0;
|
|
if(CryptAcquireContextW(&hProvider, 0, 0, PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
|
{
|
|
result = CryptGenRandom(hProvider, (DWORD)size, (BYTE*)data)? 1:0;
|
|
CryptReleaseContext(hProvider, 0);
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned int cmSystemTools::RandomSeed()
|
|
{
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
unsigned int seed = 0;
|
|
|
|
// Try using a real random source.
|
|
if(WinCryptRandom(&seed, sizeof(seed)))
|
|
{
|
|
return seed;
|
|
}
|
|
|
|
// Fall back to the time and pid.
|
|
FILETIME ft;
|
|
GetSystemTimeAsFileTime(&ft);
|
|
unsigned int t1 = static_cast<unsigned int>(ft.dwHighDateTime);
|
|
unsigned int t2 = static_cast<unsigned int>(ft.dwLowDateTime);
|
|
unsigned int pid = static_cast<unsigned int>(GetCurrentProcessId());
|
|
return t1 ^ t2 ^ pid;
|
|
#else
|
|
union
|
|
{
|
|
unsigned int integer;
|
|
char bytes[sizeof(unsigned int)];
|
|
} seed;
|
|
|
|
// Try using a real random source.
|
|
cmsys::ifstream fin("/dev/urandom");
|
|
if(fin && fin.read(seed.bytes, sizeof(seed)) &&
|
|
fin.gcount() == sizeof(seed))
|
|
{
|
|
return seed.integer;
|
|
}
|
|
|
|
// Fall back to the time and pid.
|
|
struct timeval t;
|
|
gettimeofday(&t, 0);
|
|
unsigned int pid = static_cast<unsigned int>(getpid());
|
|
unsigned int tv_sec = static_cast<unsigned int>(t.tv_sec);
|
|
unsigned int tv_usec = static_cast<unsigned int>(t.tv_usec);
|
|
// Since tv_usec never fills more than 11 bits we shift it to fill
|
|
// in the slow-changing high-order bits of tv_sec.
|
|
return tv_sec ^ (tv_usec << 21) ^ pid;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
static std::string cmSystemToolsCMakeCommand;
|
|
static std::string cmSystemToolsCTestCommand;
|
|
static std::string cmSystemToolsCPackCommand;
|
|
static std::string cmSystemToolsCMakeCursesCommand;
|
|
static std::string cmSystemToolsCMakeGUICommand;
|
|
static std::string cmSystemToolsCMakeRoot;
|
|
void cmSystemTools::FindCMakeResources(const char* argv0)
|
|
{
|
|
std::string exe_dir;
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
(void)argv0; // ignore this on windows
|
|
wchar_t modulepath[_MAX_PATH];
|
|
::GetModuleFileNameW(NULL, modulepath, sizeof(modulepath));
|
|
exe_dir =
|
|
cmSystemTools::GetFilenamePath(cmsys::Encoding::ToNarrow(modulepath));
|
|
#elif defined(__APPLE__)
|
|
(void)argv0; // ignore this on OS X
|
|
# define CM_EXE_PATH_LOCAL_SIZE 16384
|
|
char exe_path_local[CM_EXE_PATH_LOCAL_SIZE];
|
|
# if defined(MAC_OS_X_VERSION_10_3) && !defined(MAC_OS_X_VERSION_10_4)
|
|
unsigned long exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
|
|
# else
|
|
uint32_t exe_path_size = CM_EXE_PATH_LOCAL_SIZE;
|
|
# endif
|
|
# undef CM_EXE_PATH_LOCAL_SIZE
|
|
char* exe_path = exe_path_local;
|
|
if(_NSGetExecutablePath(exe_path, &exe_path_size) < 0)
|
|
{
|
|
exe_path = (char*)malloc(exe_path_size);
|
|
_NSGetExecutablePath(exe_path, &exe_path_size);
|
|
}
|
|
exe_dir =
|
|
cmSystemTools::GetFilenamePath(
|
|
cmSystemTools::GetRealPath(exe_path));
|
|
if(exe_path != exe_path_local)
|
|
{
|
|
free(exe_path);
|
|
}
|
|
if(cmSystemTools::GetFilenameName(exe_dir) == "MacOS")
|
|
{
|
|
// The executable is inside an application bundle.
|
|
// Look for ../bin (install tree) and then fall back to
|
|
// ../../../bin (build tree).
|
|
exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
|
|
if(cmSystemTools::FileExists((exe_dir+"/bin/cmake").c_str()))
|
|
{
|
|
exe_dir += "/bin";
|
|
}
|
|
else
|
|
{
|
|
exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
|
|
exe_dir = cmSystemTools::GetFilenamePath(exe_dir);
|
|
}
|
|
}
|
|
#else
|
|
std::string errorMsg;
|
|
std::string exe;
|
|
if(cmSystemTools::FindProgramPath(argv0, exe, errorMsg))
|
|
{
|
|
// remove symlinks
|
|
exe = cmSystemTools::GetRealPath(exe.c_str());
|
|
exe_dir =
|
|
cmSystemTools::GetFilenamePath(exe.c_str());
|
|
}
|
|
else
|
|
{
|
|
// ???
|
|
}
|
|
#endif
|
|
cmSystemToolsCMakeCommand = exe_dir;
|
|
cmSystemToolsCMakeCommand += "/cmake";
|
|
cmSystemToolsCMakeCommand += cmSystemTools::GetExecutableExtension();
|
|
cmSystemToolsCTestCommand = exe_dir;
|
|
cmSystemToolsCTestCommand += "/ctest";
|
|
cmSystemToolsCTestCommand += cmSystemTools::GetExecutableExtension();
|
|
cmSystemToolsCPackCommand = exe_dir;
|
|
cmSystemToolsCPackCommand += "/cpack";
|
|
cmSystemToolsCPackCommand += cmSystemTools::GetExecutableExtension();
|
|
cmSystemToolsCMakeGUICommand = exe_dir;
|
|
cmSystemToolsCMakeGUICommand += "/cmake-gui";
|
|
cmSystemToolsCMakeGUICommand += cmSystemTools::GetExecutableExtension();
|
|
if(!cmSystemTools::FileExists(cmSystemToolsCMakeGUICommand.c_str()))
|
|
{
|
|
cmSystemToolsCMakeGUICommand = "";
|
|
}
|
|
cmSystemToolsCMakeCursesCommand = exe_dir;
|
|
cmSystemToolsCMakeCursesCommand += "/ccmake";
|
|
cmSystemToolsCMakeCursesCommand += cmSystemTools::GetExecutableExtension();
|
|
if(!cmSystemTools::FileExists(cmSystemToolsCMakeCursesCommand.c_str()))
|
|
{
|
|
cmSystemToolsCMakeCursesCommand = "";
|
|
}
|
|
|
|
#ifdef CMAKE_BUILD_WITH_CMAKE
|
|
// Install tree has "<prefix>/bin/cmake" and "<prefix><CMAKE_DATA_DIR>".
|
|
std::string dir = cmSystemTools::GetFilenamePath(exe_dir);
|
|
cmSystemToolsCMakeRoot = dir + CMAKE_DATA_DIR;
|
|
if(!cmSystemTools::FileExists(
|
|
(cmSystemToolsCMakeRoot+"/Modules/CMake.cmake").c_str()))
|
|
{
|
|
// Build tree has "<build>/bin[/<config>]/cmake" and
|
|
// "<build>/CMakeFiles/CMakeSourceDir.txt".
|
|
std::string src_dir_txt = dir + "/CMakeFiles/CMakeSourceDir.txt";
|
|
cmsys::ifstream fin(src_dir_txt.c_str());
|
|
std::string src_dir;
|
|
if(fin && cmSystemTools::GetLineFromStream(fin, src_dir) &&
|
|
cmSystemTools::FileIsDirectory(src_dir.c_str()))
|
|
{
|
|
cmSystemToolsCMakeRoot = src_dir;
|
|
}
|
|
else
|
|
{
|
|
dir = cmSystemTools::GetFilenamePath(dir);
|
|
src_dir_txt = dir + "/CMakeFiles/CMakeSourceDir.txt";
|
|
cmsys::ifstream fin2(src_dir_txt.c_str());
|
|
if(fin2 && cmSystemTools::GetLineFromStream(fin2, src_dir) &&
|
|
cmSystemTools::FileIsDirectory(src_dir.c_str()))
|
|
{
|
|
cmSystemToolsCMakeRoot = src_dir;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
// Bootstrap build knows its source.
|
|
cmSystemToolsCMakeRoot = CMAKE_ROOT_DIR;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string const& cmSystemTools::GetCMakeCommand()
|
|
{
|
|
return cmSystemToolsCMakeCommand;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string const& cmSystemTools::GetCTestCommand()
|
|
{
|
|
return cmSystemToolsCTestCommand;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string const& cmSystemTools::GetCPackCommand()
|
|
{
|
|
return cmSystemToolsCPackCommand;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string const& cmSystemTools::GetCMakeCursesCommand()
|
|
{
|
|
return cmSystemToolsCMakeCursesCommand;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string const& cmSystemTools::GetCMakeGUICommand()
|
|
{
|
|
return cmSystemToolsCMakeGUICommand;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::string const& cmSystemTools::GetCMakeRoot()
|
|
{
|
|
return cmSystemToolsCMakeRoot;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
|
void cmSystemTools::MakefileColorEcho(int color, const char* message,
|
|
bool newline, bool enabled)
|
|
{
|
|
// On some platforms (an MSYS prompt) cmsysTerminal may not be able
|
|
// to determine whether the stream is displayed on a tty. In this
|
|
// case it assumes no unless we tell it otherwise. Since we want
|
|
// color messages to be displayed for users we will assume yes.
|
|
// However, we can test for some situations when the answer is most
|
|
// likely no.
|
|
int assumeTTY = cmsysTerminal_Color_AssumeTTY;
|
|
if(cmSystemTools::GetEnv("DART_TEST_FROM_DART") ||
|
|
cmSystemTools::GetEnv("DASHBOARD_TEST_FROM_CTEST") ||
|
|
cmSystemTools::GetEnv("CTEST_INTERACTIVE_DEBUG_MODE"))
|
|
{
|
|
// Avoid printing color escapes during dashboard builds.
|
|
assumeTTY = 0;
|
|
}
|
|
|
|
if(enabled)
|
|
{
|
|
cmsysTerminal_cfprintf(color | assumeTTY, stdout, "%s%s",
|
|
message, newline? "\n" : "");
|
|
}
|
|
else
|
|
{
|
|
// Color is disabled. Print without color.
|
|
fprintf(stdout, "%s%s", message, newline? "\n" : "");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::GuessLibrarySOName(std::string const& fullPath,
|
|
std::string& soname)
|
|
{
|
|
// For ELF shared libraries use a real parser to get the correct
|
|
// soname.
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
cmELF elf(fullPath.c_str());
|
|
if(elf)
|
|
{
|
|
return elf.GetSOName(soname);
|
|
}
|
|
#endif
|
|
|
|
// If the file is not a symlink we have no guess for its soname.
|
|
if(!cmSystemTools::FileIsSymlink(fullPath.c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
if(!cmSystemTools::ReadSymlink(fullPath.c_str(), soname))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If the symlink has a path component we have no guess for the soname.
|
|
if(!cmSystemTools::GetFilenamePath(soname).empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// If the symlink points at an extended version of the same name
|
|
// assume it is the soname.
|
|
std::string name = cmSystemTools::GetFilenameName(fullPath);
|
|
if(soname.length() > name.length() &&
|
|
soname.substr(0, name.length()) == name)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::GuessLibraryInstallName(std::string const& fullPath,
|
|
std::string& soname)
|
|
{
|
|
std::vector<cmStdString> cmds;
|
|
cmds.push_back("otool");
|
|
cmds.push_back("-D");
|
|
cmds.push_back(fullPath.c_str());
|
|
|
|
std::string output;
|
|
if(!RunSingleCommand(cmds, &output, 0, 0, OUTPUT_NONE))
|
|
{
|
|
cmds.insert(cmds.begin(), "-r");
|
|
cmds.insert(cmds.begin(), "xcrun");
|
|
if(!RunSingleCommand(cmds, &output, 0, 0, OUTPUT_NONE))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> strs = cmSystemTools::tokenize(output, "\n");
|
|
// otool returns extra lines reporting multiple install names
|
|
// in case the binary is multi-arch and none of the architectures
|
|
// is native (e.g. i386;ppc on x86_64)
|
|
if(strs.size() >= 2)
|
|
{
|
|
soname = strs[1];
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
std::string::size_type cmSystemToolsFindRPath(std::string const& have,
|
|
std::string const& want)
|
|
{
|
|
// Search for the desired rpath.
|
|
std::string::size_type pos = have.find(want);
|
|
|
|
// If the path is not present we are done.
|
|
if(pos == std::string::npos)
|
|
{
|
|
return pos;
|
|
}
|
|
|
|
// Build a regex to match a properly separated path instance.
|
|
std::string regex_str = "(^|:)(";
|
|
for(std::string::const_iterator i = want.begin(); i != want.end(); ++i)
|
|
{
|
|
int ch = *i;
|
|
if(!(('a' <= ch && ch <= 'z') ||
|
|
('A' <= ch && ch <= 'Z') ||
|
|
('0' <= ch && ch <= '9')))
|
|
{
|
|
// Escape the non-alphanumeric character.
|
|
regex_str += "\\";
|
|
}
|
|
// Store the character.
|
|
regex_str.append(1, static_cast<char>(ch));
|
|
}
|
|
regex_str += ")(:|$)";
|
|
|
|
// Look for the separated path.
|
|
cmsys::RegularExpression regex(regex_str.c_str());
|
|
if(regex.find(have))
|
|
{
|
|
// Return the position of the path portion.
|
|
return regex.start(2);
|
|
}
|
|
else
|
|
{
|
|
// The desired rpath was not found.
|
|
return std::string::npos;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
struct cmSystemToolsRPathInfo
|
|
{
|
|
unsigned long Position;
|
|
unsigned long Size;
|
|
std::string Name;
|
|
std::string Value;
|
|
};
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::ChangeRPath(std::string const& file,
|
|
std::string const& oldRPath,
|
|
std::string const& newRPath,
|
|
std::string* emsg,
|
|
bool* changed)
|
|
{
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
if(changed)
|
|
{
|
|
*changed = false;
|
|
}
|
|
int rp_count = 0;
|
|
cmSystemToolsRPathInfo rp[2];
|
|
{
|
|
// Parse the ELF binary.
|
|
cmELF elf(file.c_str());
|
|
|
|
// Get the RPATH and RUNPATH entries from it.
|
|
int se_count = 0;
|
|
cmELF::StringEntry const* se[2] = {0, 0};
|
|
const char* se_name[2] = {0, 0};
|
|
if(cmELF::StringEntry const* se_rpath = elf.GetRPath())
|
|
{
|
|
se[se_count] = se_rpath;
|
|
se_name[se_count] = "RPATH";
|
|
++se_count;
|
|
}
|
|
if(cmELF::StringEntry const* se_runpath = elf.GetRunPath())
|
|
{
|
|
se[se_count] = se_runpath;
|
|
se_name[se_count] = "RUNPATH";
|
|
++se_count;
|
|
}
|
|
if(se_count == 0)
|
|
{
|
|
if(newRPath.empty())
|
|
{
|
|
// The new rpath is empty and there is no rpath anyway so it is
|
|
// okay.
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "No valid ELF RPATH or RUNPATH entry exists in the file; ";
|
|
*emsg += elf.GetErrorMessage();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for(int i=0; i < se_count; ++i)
|
|
{
|
|
// If both RPATH and RUNPATH refer to the same string literal it
|
|
// needs to be changed only once.
|
|
if(rp_count && rp[0].Position == se[i]->Position)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Make sure the current rpath contains the old rpath.
|
|
std::string::size_type pos =
|
|
cmSystemToolsFindRPath(se[i]->Value, oldRPath);
|
|
if(pos == std::string::npos)
|
|
{
|
|
// If it contains the new rpath instead then it is okay.
|
|
if(cmSystemToolsFindRPath(se[i]->Value, newRPath) != std::string::npos)
|
|
{
|
|
continue;
|
|
}
|
|
if(emsg)
|
|
{
|
|
cmOStringStream e;
|
|
e << "The current " << se_name[i] << " is:\n"
|
|
<< " " << se[i]->Value << "\n"
|
|
<< "which does not contain:\n"
|
|
<< " " << oldRPath << "\n"
|
|
<< "as was expected.";
|
|
*emsg = e.str();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Store information about the entry in the file.
|
|
rp[rp_count].Position = se[i]->Position;
|
|
rp[rp_count].Size = se[i]->Size;
|
|
rp[rp_count].Name = se_name[i];
|
|
|
|
// Construct the new value which preserves the part of the path
|
|
// not being changed.
|
|
rp[rp_count].Value = se[i]->Value.substr(0, pos);
|
|
rp[rp_count].Value += newRPath;
|
|
rp[rp_count].Value += se[i]->Value.substr(pos+oldRPath.length(),
|
|
oldRPath.npos);
|
|
|
|
// Make sure there is enough room to store the new rpath and at
|
|
// least one null terminator.
|
|
if(rp[rp_count].Size < rp[rp_count].Value.length()+1)
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "The replacement path is too long for the ";
|
|
*emsg += se_name[i];
|
|
*emsg += " entry.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// This entry is ready for update.
|
|
++rp_count;
|
|
}
|
|
}
|
|
|
|
// If no runtime path needs to be changed, we are done.
|
|
if(rp_count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
{
|
|
// Open the file for update.
|
|
cmsys::ofstream f(file.c_str(),
|
|
std::ios::in | std::ios::out | std::ios::binary);
|
|
if(!f)
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error opening file for update.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Store the new RPATH and RUNPATH strings.
|
|
for(int i=0; i < rp_count; ++i)
|
|
{
|
|
// Seek to the RPATH position.
|
|
if(!f.seekp(rp[i].Position))
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error seeking to ";
|
|
*emsg += rp[i].Name;
|
|
*emsg += " position.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Write the new rpath. Follow it with enough null terminators to
|
|
// fill the string table entry.
|
|
f << rp[i].Value;
|
|
for(unsigned long j=rp[i].Value.length(); j < rp[i].Size; ++j)
|
|
{
|
|
f << '\0';
|
|
}
|
|
|
|
// Make sure it wrote correctly.
|
|
if(!f)
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error writing the new ";
|
|
*emsg += rp[i].Name;
|
|
*emsg += " string to the file.";
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Everything was updated successfully.
|
|
if(changed)
|
|
{
|
|
*changed = true;
|
|
}
|
|
return true;
|
|
#else
|
|
(void)file;
|
|
(void)oldRPath;
|
|
(void)newRPath;
|
|
(void)emsg;
|
|
(void)changed;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
|
|
const char* lhss, const char* rhss)
|
|
{
|
|
// Parse out up to 8 components.
|
|
unsigned int lhs[8] = {0,0,0,0,0,0,0,0};
|
|
unsigned int rhs[8] = {0,0,0,0,0,0,0,0};
|
|
sscanf(lhss, "%u.%u.%u.%u.%u.%u.%u.%u",
|
|
&lhs[0], &lhs[1], &lhs[2], &lhs[3],
|
|
&lhs[4], &lhs[5], &lhs[6], &lhs[7]);
|
|
sscanf(rhss, "%u.%u.%u.%u.%u.%u.%u.%u",
|
|
&rhs[0], &rhs[1], &rhs[2], &rhs[3],
|
|
&rhs[4], &rhs[5], &rhs[6], &rhs[7]);
|
|
|
|
// Do component-wise comparison.
|
|
for(unsigned int i=0; i < 8; ++i)
|
|
{
|
|
if(lhs[i] < rhs[i])
|
|
{
|
|
// lhs < rhs, so true if operation is LESS
|
|
return op == cmSystemTools::OP_LESS;
|
|
}
|
|
else if(lhs[i] > rhs[i])
|
|
{
|
|
// lhs > rhs, so true if operation is GREATER
|
|
return op == cmSystemTools::OP_GREATER;
|
|
}
|
|
}
|
|
// lhs == rhs, so true if operation is EQUAL
|
|
return op == cmSystemTools::OP_EQUAL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
|
|
bool* removed)
|
|
{
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
if(removed)
|
|
{
|
|
*removed = false;
|
|
}
|
|
int zeroCount = 0;
|
|
unsigned long zeroPosition[2] = {0,0};
|
|
unsigned long zeroSize[2] = {0,0};
|
|
unsigned long bytesBegin = 0;
|
|
std::vector<char> bytes;
|
|
{
|
|
// Parse the ELF binary.
|
|
cmELF elf(file.c_str());
|
|
|
|
// Get the RPATH and RUNPATH entries from it and sort them by index
|
|
// in the dynamic section header.
|
|
int se_count = 0;
|
|
cmELF::StringEntry const* se[2] = {0, 0};
|
|
if(cmELF::StringEntry const* se_rpath = elf.GetRPath())
|
|
{
|
|
se[se_count++] = se_rpath;
|
|
}
|
|
if(cmELF::StringEntry const* se_runpath = elf.GetRunPath())
|
|
{
|
|
se[se_count++] = se_runpath;
|
|
}
|
|
if(se_count == 0)
|
|
{
|
|
// There is no RPATH or RUNPATH anyway.
|
|
return true;
|
|
}
|
|
if(se_count == 2 && se[1]->IndexInSection < se[0]->IndexInSection)
|
|
{
|
|
cmsys_stl::swap(se[0], se[1]);
|
|
}
|
|
|
|
// Get the size of the dynamic section header.
|
|
unsigned int count = elf.GetDynamicEntryCount();
|
|
if(count == 0)
|
|
{
|
|
// This should happen only for invalid ELF files where a DT_NULL
|
|
// appears before the end of the table.
|
|
if(emsg)
|
|
{
|
|
*emsg = "DYNAMIC section contains a DT_NULL before the end.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Save information about the string entries to be zeroed.
|
|
zeroCount = se_count;
|
|
for(int i=0; i < se_count; ++i)
|
|
{
|
|
zeroPosition[i] = se[i]->Position;
|
|
zeroSize[i] = se[i]->Size;
|
|
}
|
|
|
|
// Get the range of file positions corresponding to each entry and
|
|
// the rest of the table after them.
|
|
unsigned long entryBegin[3] = {0,0,0};
|
|
unsigned long entryEnd[2] = {0,0};
|
|
for(int i=0; i < se_count; ++i)
|
|
{
|
|
entryBegin[i] = elf.GetDynamicEntryPosition(se[i]->IndexInSection);
|
|
entryEnd[i] = elf.GetDynamicEntryPosition(se[i]->IndexInSection+1);
|
|
}
|
|
entryBegin[se_count] = elf.GetDynamicEntryPosition(count);
|
|
|
|
// The data are to be written over the old table entries starting at
|
|
// the first one being removed.
|
|
bytesBegin = entryBegin[0];
|
|
unsigned long bytesEnd = entryBegin[se_count];
|
|
|
|
// Allocate a buffer to hold the part of the file to be written.
|
|
// Initialize it with zeros.
|
|
bytes.resize(bytesEnd - bytesBegin, 0);
|
|
|
|
// Read the part of the DYNAMIC section header that will move.
|
|
// The remainder of the buffer will be left with zeros which
|
|
// represent a DT_NULL entry.
|
|
char* data = &bytes[0];
|
|
for(int i=0; i < se_count; ++i)
|
|
{
|
|
// Read data between the entries being removed.
|
|
unsigned long sz = entryBegin[i+1] - entryEnd[i];
|
|
if(sz > 0 && !elf.ReadBytes(entryEnd[i], sz, data))
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Failed to read DYNAMIC section header.";
|
|
}
|
|
return false;
|
|
}
|
|
data += sz;
|
|
}
|
|
}
|
|
|
|
// Open the file for update.
|
|
cmsys::ofstream f(file.c_str(),
|
|
std::ios::in | std::ios::out | std::ios::binary);
|
|
if(!f)
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error opening file for update.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Write the new DYNAMIC table header.
|
|
if(!f.seekp(bytesBegin))
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error seeking to DYNAMIC table header for RPATH.";
|
|
}
|
|
return false;
|
|
}
|
|
if(!f.write(&bytes[0], bytes.size()))
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error replacing DYNAMIC table header.";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Fill the RPATH and RUNPATH strings with zero bytes.
|
|
for(int i=0; i < zeroCount; ++i)
|
|
{
|
|
if(!f.seekp(zeroPosition[i]))
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error seeking to RPATH position.";
|
|
}
|
|
return false;
|
|
}
|
|
for(unsigned long j=0; j < zeroSize[i]; ++j)
|
|
{
|
|
f << '\0';
|
|
}
|
|
if(!f)
|
|
{
|
|
if(emsg)
|
|
{
|
|
*emsg = "Error writing the empty rpath string to the file.";
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Everything was updated successfully.
|
|
if(removed)
|
|
{
|
|
*removed = true;
|
|
}
|
|
return true;
|
|
#else
|
|
(void)file;
|
|
(void)emsg;
|
|
(void)removed;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::CheckRPath(std::string const& file,
|
|
std::string const& newRPath)
|
|
{
|
|
#if defined(CMAKE_USE_ELF_PARSER)
|
|
// Parse the ELF binary.
|
|
cmELF elf(file.c_str());
|
|
|
|
// Get the RPATH or RUNPATH entry from it.
|
|
cmELF::StringEntry const* se = elf.GetRPath();
|
|
if(!se)
|
|
{
|
|
se = elf.GetRunPath();
|
|
}
|
|
|
|
// Make sure the current rpath contains the new rpath.
|
|
if(newRPath.empty())
|
|
{
|
|
if(!se)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(se &&
|
|
cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
#else
|
|
(void)file;
|
|
(void)newRPath;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool cmSystemTools::RepeatedRemoveDirectory(const char* dir)
|
|
{
|
|
// Windows sometimes locks files temporarily so try a few times.
|
|
for(int i = 0; i < 10; ++i)
|
|
{
|
|
if(cmSystemTools::RemoveADirectory(dir))
|
|
{
|
|
return true;
|
|
}
|
|
cmSystemTools::Delay(100);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::vector<std::string> cmSystemTools::tokenize(const std::string& str,
|
|
const std::string& sep)
|
|
{
|
|
std::vector<std::string> tokens;
|
|
std::string::size_type tokend = 0;
|
|
|
|
do
|
|
{
|
|
std::string::size_type tokstart=str.find_first_not_of(sep, tokend);
|
|
if (tokstart==std::string::npos)
|
|
{
|
|
break; // no more tokens
|
|
}
|
|
tokend=str.find_first_of(sep,tokstart);
|
|
if (tokend==std::string::npos)
|
|
{
|
|
tokens.push_back(str.substr(tokstart));
|
|
}
|
|
else
|
|
{
|
|
tokens.push_back(str.substr(tokstart,tokend-tokstart));
|
|
}
|
|
} while (tokend!=std::string::npos);
|
|
|
|
if (tokens.empty())
|
|
{
|
|
tokens.push_back("");
|
|
}
|
|
return tokens;
|
|
}
|