/****************************************************************************+ −
**+ −
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the tools applications of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, this file may be used under the terms of the GNU Lesser+ −
** General Public License version 2.1 as published by the Free Software+ −
** Foundation and appearing in the file LICENSE.LGPL included in the+ −
** packaging of this file. Please review the following information to+ −
** ensure the GNU Lesser General Public License version 2.1 requirements+ −
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.+ −
**+ −
** In addition, as a special exception, Nokia gives you certain additional+ −
** rights. These rights are described in the Nokia Qt LGPL Exception+ −
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
+ −
#include "environment.h"+ −
+ −
#include <process.h>+ −
#include <iostream>+ −
#include <qdebug.h>+ −
#include <QDir>+ −
#include <QStringList>+ −
#include <QMap>+ −
#include <QDir>+ −
#include <QFile>+ −
#include <QFileInfo>+ −
+ −
//#define CONFIGURE_DEBUG_EXECUTE+ −
//#define CONFIGURE_DEBUG_CP_DIR+ −
+ −
using namespace std;+ −
+ −
#ifdef Q_OS_WIN32+ −
#include <qt_windows.h>+ −
#endif+ −
+ −
#include <symbian/epocroot.h> // from tools/shared+ −
#include <windows/registry.h> // from tools/shared+ −
+ −
QT_BEGIN_NAMESPACE+ −
+ −
struct CompilerInfo{+ −
Compiler compiler;+ −
const char *compilerStr;+ −
const char *regKey;+ −
const char *executable;+ −
} compiler_info[] = {+ −
// The compilers here are sorted in a reversed-preferred order+ −
{CC_BORLAND, "Borland C++", 0, "bcc32.exe"},+ −
{CC_MINGW, "MinGW (Minimalist GNU for Windows)", 0, "mingw32-gcc.exe"},+ −
{CC_INTEL, "Intel(R) C++ Compiler for 32-bit applications", 0, "icl.exe"}, // xilink.exe, xilink5.exe, xilink6.exe, xilib.exe+ −
{CC_MSVC6, "Microsoft (R) 32-bit C/C++ Optimizing Compiler (6.x)", "Software\\Microsoft\\VisualStudio\\6.0\\Setup\\Microsoft Visual C++\\ProductDir", "cl.exe"}, // link.exe, lib.exe+ −
{CC_NET2002, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2002 (7.0)", "Software\\Microsoft\\VisualStudio\\7.0\\Setup\\VC\\ProductDir", "cl.exe"}, // link.exe, lib.exe+ −
{CC_NET2003, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2003 (7.1)", "Software\\Microsoft\\VisualStudio\\7.1\\Setup\\VC\\ProductDir", "cl.exe"}, // link.exe, lib.exe+ −
{CC_NET2005, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2005 (8.0)", "Software\\Microsoft\\VisualStudio\\SxS\\VC7\\8.0", "cl.exe"}, // link.exe, lib.exe+ −
{CC_NET2008, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2008 (9.0)", "Software\\Microsoft\\VisualStudio\\SxS\\VC7\\9.0", "cl.exe"}, // link.exe, lib.exe+ −
{CC_UNKNOWN, "Unknown", 0, 0},+ −
};+ −
+ −
+ −
// Initialize static variables+ −
Compiler Environment::detectedCompiler = CC_UNKNOWN;+ −
+ −
/*!+ −
Returns the pointer to the CompilerInfo for a \a compiler.+ −
*/+ −
CompilerInfo *Environment::compilerInfo(Compiler compiler)+ −
{+ −
int i = 0;+ −
while(compiler_info[i].compiler != compiler && compiler_info[i].compiler != CC_UNKNOWN)+ −
++i;+ −
return &(compiler_info[i]);+ −
}+ −
+ −
/*!+ −
Returns the qmakespec for the compiler detected on the system.+ −
*/+ −
QString Environment::detectQMakeSpec()+ −
{+ −
QString spec;+ −
switch (detectCompiler()) {+ −
case CC_NET2008:+ −
spec = "win32-msvc2008";+ −
break;+ −
case CC_NET2005:+ −
spec = "win32-msvc2005";+ −
break;+ −
case CC_NET2003:+ −
spec = "win32-msvc2003";+ −
break;+ −
case CC_NET2002:+ −
spec = "win32-msvc2002";+ −
break;+ −
case CC_MSVC4:+ −
case CC_MSVC5:+ −
case CC_MSVC6:+ −
spec = "win32-msvc";+ −
break;+ −
case CC_INTEL:+ −
spec = "win32-icc";+ −
break;+ −
case CC_MINGW:+ −
spec = "win32-g++";+ −
break;+ −
case CC_BORLAND:+ −
spec = "win32-borland";+ −
break;+ −
default:+ −
break;+ −
}+ −
+ −
return spec;+ −
}+ −
+ −
/*!+ −
Returns the enum of the compiler which was detected on the system.+ −
The compilers are detected in the order as entered into the+ −
compiler_info list.+ −
+ −
If more than one compiler is found, CC_UNKNOWN is returned.+ −
*/+ −
Compiler Environment::detectCompiler()+ −
{+ −
#ifndef Q_OS_WIN32+ −
return MSVC6; // Always generate MSVC 6.0 versions on other platforms+ −
#else+ −
if(detectedCompiler != CC_UNKNOWN)+ −
return detectedCompiler;+ −
+ −
int installed = 0;+ −
+ −
// Check for compilers in registry first, to see which version is in PATH+ −
QString paths = qgetenv("PATH");+ −
QStringList pathlist = paths.toLower().split(";");+ −
for(int i = 0; compiler_info[i].compiler; ++i) {+ −
QString productPath = readRegistryKey(HKEY_LOCAL_MACHINE, compiler_info[i].regKey).toLower();+ −
if (productPath.length()) {+ −
QStringList::iterator it;+ −
for(it = pathlist.begin(); it != pathlist.end(); ++it) {+ −
if((*it).contains(productPath)) {+ −
++installed;+ −
detectedCompiler = compiler_info[i].compiler;+ −
break;+ −
}+ −
}+ −
}+ −
}+ −
+ −
// Now just go looking for the executables, and accept any executable as the lowest version+ −
if (!installed) {+ −
for(int i = 0; compiler_info[i].compiler; ++i) {+ −
QString executable = QString(compiler_info[i].executable).toLower();+ −
if (executable.length() && Environment::detectExecutable(executable)) {+ −
++installed;+ −
detectedCompiler = compiler_info[i].compiler;+ −
break;+ −
}+ −
}+ −
}+ −
+ −
if (installed > 1) {+ −
cout << "Found more than one known compiler! Using \"" << compilerInfo(detectedCompiler)->compilerStr << "\"" << endl;+ −
detectedCompiler = CC_UNKNOWN;+ −
}+ −
return detectedCompiler;+ −
#endif+ −
};+ −
+ −
/*!+ −
Returns true if the \a executable could be loaded, else false.+ −
This means that the executable either is in the current directory+ −
or in the PATH.+ −
*/+ −
bool Environment::detectExecutable(const QString &executable)+ −
{+ −
PROCESS_INFORMATION procInfo;+ −
memset(&procInfo, 0, sizeof(procInfo));+ −
+ −
STARTUPINFO startInfo;+ −
memset(&startInfo, 0, sizeof(startInfo));+ −
startInfo.cb = sizeof(startInfo);+ −
+ −
bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),+ −
0, 0, false,+ −
CREATE_NO_WINDOW | CREATE_SUSPENDED,+ −
0, 0, &startInfo, &procInfo);+ −
+ −
if (couldExecute) {+ −
CloseHandle(procInfo.hThread);+ −
TerminateProcess(procInfo.hProcess, 0);+ −
CloseHandle(procInfo.hProcess);+ −
}+ −
return couldExecute;+ −
}+ −
+ −
/*!+ −
Creates a commandling from \a program and it \a arguments,+ −
escaping characters that needs it.+ −
*/+ −
static QString qt_create_commandline(const QString &program, const QStringList &arguments)+ −
{+ −
QString programName = program;+ −
if (!programName.startsWith("\"") && !programName.endsWith("\"") && programName.contains(" "))+ −
programName = "\"" + programName + "\"";+ −
programName.replace("/", "\\");+ −
+ −
QString args;+ −
// add the prgram as the first arrg ... it works better+ −
args = programName + " ";+ −
for (int i=0; i<arguments.size(); ++i) {+ −
QString tmp = arguments.at(i);+ −
// in the case of \" already being in the string the \ must also be escaped+ −
tmp.replace( "\\\"", "\\\\\"" );+ −
// escape a single " because the arguments will be parsed+ −
tmp.replace( "\"", "\\\"" );+ −
if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\t')) {+ −
// The argument must not end with a \ since this would be interpreted+ −
// as escaping the quote -- rather put the \ behind the quote: e.g.+ −
// rather use "foo"\ than "foo\"+ −
QString endQuote("\"");+ −
int i = tmp.length();+ −
while (i>0 && tmp.at(i-1) == '\\') {+ −
--i;+ −
endQuote += "\\";+ −
}+ −
args += QString(" \"") + tmp.left(i) + endQuote;+ −
} else {+ −
args += ' ' + tmp;+ −
}+ −
}+ −
return args;+ −
}+ −
+ −
/*!+ −
Creates a QByteArray of the \a environment.+ −
*/+ −
static QByteArray qt_create_environment(const QStringList &environment)+ −
{+ −
QByteArray envlist;+ −
if (environment.isEmpty())+ −
return envlist;+ −
+ −
int pos = 0;+ −
// add PATH if necessary (for DLL loading)+ −
QByteArray path = qgetenv("PATH");+ −
if (environment.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty() && !path.isNull()) {+ −
QString tmp = QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path));+ −
uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);+ −
envlist.resize(envlist.size() + tmpSize);+ −
memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);+ −
pos += tmpSize;+ −
}+ −
// add the user environment+ −
for (QStringList::ConstIterator it = environment.begin(); it != environment.end(); it++ ) {+ −
QString tmp = *it;+ −
uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);+ −
envlist.resize(envlist.size() + tmpSize);+ −
memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);+ −
pos += tmpSize;+ −
}+ −
// add the 2 terminating 0 (actually 4, just to be on the safe side)+ −
envlist.resize(envlist.size() + 4);+ −
envlist[pos++] = 0;+ −
envlist[pos++] = 0;+ −
envlist[pos++] = 0;+ −
envlist[pos++] = 0;+ −
+ −
return envlist;+ −
}+ −
+ −
/*!+ −
Executes the command described in \a arguments, in the+ −
environment inherited from the parent process, with the+ −
\a additionalEnv settings applied.+ −
\a removeEnv removes the specified environment variables from+ −
the environment of the executed process.+ −
+ −
Returns the exit value of the process, or -1 if the command could+ −
not be executed.+ −
+ −
This function uses _(w)spawnvpe to spawn a process by searching+ −
through the PATH environment variable.+ −
*/+ −
int Environment::execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv)+ −
{+ −
#ifdef CONFIGURE_DEBUG_EXECUTE+ −
qDebug() << "About to Execute: " << arguments;+ −
qDebug() << " " << QDir::currentPath();+ −
qDebug() << " " << additionalEnv;+ −
qDebug() << " " << removeEnv;+ −
#endif+ −
// Create the full environment from the current environment and+ −
// the additionalEnv strings, then remove all variables defined+ −
// in removeEnv+ −
QMap<QString, QString> fullEnvMap;+ −
LPWSTR envStrings = GetEnvironmentStrings();+ −
if (envStrings) {+ −
int strLen = 0;+ −
for (LPWSTR envString = envStrings; *(envString); envString += strLen + 1) {+ −
strLen = wcslen(envString);+ −
QString str = QString((const QChar*)envString, strLen);+ −
if (!str.startsWith("=")) { // These are added by the system+ −
int sepIndex = str.indexOf('=');+ −
fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));+ −
}+ −
}+ −
}+ −
FreeEnvironmentStrings(envStrings);+ −
+ −
// Add additionalEnv variables+ −
for (int i = 0; i < additionalEnv.count(); ++i) {+ −
const QString &str = additionalEnv.at(i);+ −
int sepIndex = str.indexOf('=');+ −
fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));+ −
}+ −
+ −
// Remove removeEnv variables+ −
for (int j = 0; j < removeEnv.count(); ++j)+ −
fullEnvMap.remove(removeEnv.at(j).toUpper());+ −
+ −
// Add all variables to a QStringList+ −
QStringList fullEnv;+ −
QMapIterator<QString, QString> it(fullEnvMap);+ −
while (it.hasNext()) {+ −
it.next();+ −
fullEnv += QString(it.key() + "=" + it.value());+ −
}+ −
+ −
// ----------------------------+ −
QString program = arguments.takeAt(0);+ −
QString args = qt_create_commandline(program, arguments);+ −
QByteArray envlist = qt_create_environment(fullEnv);+ −
+ −
DWORD exitCode = -1;+ −
PROCESS_INFORMATION procInfo;+ −
memset(&procInfo, 0, sizeof(procInfo));+ −
+ −
STARTUPINFO startInfo;+ −
memset(&startInfo, 0, sizeof(startInfo));+ −
startInfo.cb = sizeof(startInfo);+ −
+ −
bool couldExecute = CreateProcess(0, (wchar_t*)args.utf16(),+ −
0, 0, true, CREATE_UNICODE_ENVIRONMENT,+ −
envlist.isEmpty() ? 0 : envlist.data(),+ −
0, &startInfo, &procInfo);+ −
+ −
if (couldExecute) {+ −
WaitForSingleObject(procInfo.hProcess, INFINITE);+ −
GetExitCodeProcess(procInfo.hProcess, &exitCode);+ −
CloseHandle(procInfo.hThread);+ −
CloseHandle(procInfo.hProcess);+ −
}+ −
+ −
+ −
if (exitCode == -1) {+ −
switch(GetLastError()) {+ −
case E2BIG:+ −
cerr << "execute: Argument list exceeds 1024 bytes" << endl;+ −
foreach(QString arg, arguments)+ −
cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;+ −
break;+ −
case ENOENT:+ −
cerr << "execute: File or path is not found (" << program.toLocal8Bit().constData() << ")" << endl;+ −
break;+ −
case ENOEXEC:+ −
cerr << "execute: Specified file is not executable or has invalid executable-file format (" << program.toLocal8Bit().constData() << ")" << endl;+ −
break;+ −
case ENOMEM:+ −
cerr << "execute: Not enough memory is available to execute new process." << endl;+ −
break;+ −
default:+ −
cerr << "execute: Unknown error" << endl;+ −
foreach(QString arg, arguments)+ −
cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;+ −
break;+ −
}+ −
}+ −
return exitCode;+ −
}+ −
+ −
bool Environment::cpdir(const QString &srcDir, const QString &destDir)+ −
{+ −
QString cleanSrcName = QDir::cleanPath(srcDir);+ −
QString cleanDstName = QDir::cleanPath(destDir);+ −
#ifdef CONFIGURE_DEBUG_CP_DIR+ −
qDebug() << "Attempt to cpdir " << cleanSrcName << "->" << cleanDstName;+ −
#endif+ −
if(!QFile::exists(cleanDstName) && !QDir().mkpath(cleanDstName)) {+ −
qDebug() << "cpdir: Failure to create " << cleanDstName;+ −
return false;+ −
}+ −
+ −
bool result = true;+ −
QDir dir = QDir(cleanSrcName);+ −
QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);+ −
for (int i = 0; result && (i < allEntries.count()); ++i) {+ −
QFileInfo entry = allEntries.at(i);+ −
bool intermediate = true;+ −
if (entry.isDir()) {+ −
intermediate = cpdir(QString("%1/%2").arg(cleanSrcName).arg(entry.fileName()),+ −
QString("%1/%2").arg(cleanDstName).arg(entry.fileName()));+ −
} else {+ −
QString destFile = QString("%1/%2").arg(cleanDstName).arg(entry.fileName());+ −
#ifdef CONFIGURE_DEBUG_CP_DIR+ −
qDebug() << "About to cp (file)" << entry.absoluteFilePath() << "->" << destFile;+ −
#endif+ −
QFile::remove(destFile);+ −
intermediate = QFile::copy(entry.absoluteFilePath(), destFile);+ −
SetFileAttributes((wchar_t*)destFile.utf16(), FILE_ATTRIBUTE_NORMAL);+ −
}+ −
if(!intermediate) {+ −
qDebug() << "cpdir: Failure for " << entry.fileName() << entry.isDir();+ −
result = false;+ −
}+ −
}+ −
return result;+ −
}+ −
+ −
bool Environment::rmdir(const QString &name)+ −
{+ −
bool result = true;+ −
QString cleanName = QDir::cleanPath(name);+ −
+ −
QDir dir = QDir(cleanName);+ −
QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);+ −
for (int i = 0; result && (i < allEntries.count()); ++i) {+ −
QFileInfo entry = allEntries.at(i);+ −
if (entry.isDir()) {+ −
result &= rmdir(entry.absoluteFilePath());+ −
} else {+ −
result &= QFile::remove(entry.absoluteFilePath());+ −
}+ −
}+ −
result &= dir.rmdir(cleanName);+ −
return result;+ −
}+ −
+ −
QString Environment::symbianEpocRoot()+ −
{+ −
// Call function defined in tools/shared/symbian/epocroot.h+ −
return ::epocRoot();+ −
}+ −
+ −
QT_END_NAMESPACE+ −