1
/****************************************************************************
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of the tools applications of the Qt Toolkit.
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file. Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
** In addition, as a special exception, Nokia gives you certain additional
25
** rights. These rights are described in the Nokia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
40
****************************************************************************/
42
#include "environment.h"
48
#include <QStringList>
54
//#define CONFIGURE_DEBUG_EXECUTE
55
//#define CONFIGURE_DEBUG_CP_DIR
60
#include <qt_windows.h>
68
const char *compilerStr;
70
const char *executable;
72
// The compilers here are sorted in a reversed-preferred order
73
{CC_BORLAND, "Borland C++", 0, "bcc32.exe"},
74
{CC_MINGW, "MinGW (Minimalist GNU for Windows)", 0, "mingw32-gcc.exe"},
75
{CC_INTEL, "Intel(R) C++ Compiler for 32-bit applications", 0, "icl.exe"}, // xilink.exe, xilink5.exe, xilink6.exe, xilib.exe
76
{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
77
{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
78
{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
79
{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
80
{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
81
{CC_UNKNOWN, "Unknown", 0, 0},
85
// Initialize static variables
86
Compiler Environment::detectedCompiler = CC_UNKNOWN;
89
Returns the pointer to the CompilerInfo for a \a compiler.
91
CompilerInfo *Environment::compilerInfo(Compiler compiler)
94
while(compiler_info[i].compiler != compiler && compiler_info[i].compiler != CC_UNKNOWN)
96
return &(compiler_info[i]);
100
Returns the path part of a registry key.
103
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"
105
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\"
107
QString Environment::keyPath(const QString &rKey)
109
int idx = rKey.lastIndexOf(QLatin1Char('\\'));
112
return rKey.left(idx + 1);
116
Returns the name part of a registry key.
119
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"
123
QString Environment::keyName(const QString &rKey)
125
int idx = rKey.lastIndexOf(QLatin1Char('\\'));
129
QString res(rKey.mid(idx + 1));
130
if (res == "Default" || res == ".")
136
Returns a registry keys value in string form.
137
If the registry key does not exist, or cannot be accessed, a
138
QString() is returned.
140
QString Environment::readRegistryKey(HKEY parentHandle, const QString &rSubkey)
145
QString rSubkeyName = keyName(rSubkey);
146
QString rSubkeyPath = keyPath(rSubkey);
149
LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0, KEY_READ, &handle);
150
if (res != ERROR_SUCCESS)
153
// get the size and type of the value
156
res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, &dataType, 0, &dataSize);
157
if (res != ERROR_SUCCESS) {
163
QByteArray data(dataSize, 0);
164
res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), 0, 0,
165
reinterpret_cast<unsigned char*>(data.data()), &dataSize);
166
if (res != ERROR_SUCCESS) {
175
result = QString::fromWCharArray(((const wchar_t *)data.constData()));
183
QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i);
190
result = l.join(", ");
196
result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2);
200
case REG_DWORD_BIG_ENDIAN:
202
Q_ASSERT(data.size() == sizeof(int));
204
memcpy((char*)&i, data.constData(), sizeof(int));
205
result = QString::number(i);
210
qWarning("QSettings: unknown data %d type in windows registry", dataType);
220
Returns the qmakespec for the compiler detected on the system.
222
QString Environment::detectQMakeSpec()
225
switch (detectCompiler()) {
227
spec = "win32-msvc2008";
230
spec = "win32-msvc2005";
233
spec = "win32-msvc2003";
236
spec = "win32-msvc2002";
250
spec = "win32-borland";
260
Returns the enum of the compiler which was detected on the system.
261
The compilers are detected in the order as entered into the
264
If more than one compiler is found, CC_UNKNOWN is returned.
266
Compiler Environment::detectCompiler()
269
return MSVC6; // Always generate MSVC 6.0 versions on other platforms
271
if(detectedCompiler != CC_UNKNOWN)
272
return detectedCompiler;
276
// Check for compilers in registry first, to see which version is in PATH
277
QString paths = qgetenv("PATH");
278
QStringList pathlist = paths.toLower().split(";");
279
for(int i = 0; compiler_info[i].compiler; ++i) {
280
QString productPath = readRegistryKey(HKEY_LOCAL_MACHINE, compiler_info[i].regKey).toLower();
281
if (productPath.length()) {
282
QStringList::iterator it;
283
for(it = pathlist.begin(); it != pathlist.end(); ++it) {
284
if((*it).contains(productPath)) {
286
detectedCompiler = compiler_info[i].compiler;
293
// Now just go looking for the executables, and accept any executable as the lowest version
295
for(int i = 0; compiler_info[i].compiler; ++i) {
296
QString executable = QString(compiler_info[i].executable).toLower();
297
if (executable.length() && Environment::detectExecutable(executable)) {
299
detectedCompiler = compiler_info[i].compiler;
306
cout << "Found more than one known compiler! Using \"" << compilerInfo(detectedCompiler)->compilerStr << "\"" << endl;
307
detectedCompiler = CC_UNKNOWN;
309
return detectedCompiler;
314
Returns true if the \a executable could be loaded, else false.
315
This means that the executable either is in the current directory
318
bool Environment::detectExecutable(const QString &executable)
320
PROCESS_INFORMATION procInfo;
321
memset(&procInfo, 0, sizeof(procInfo));
323
STARTUPINFO startInfo;
324
memset(&startInfo, 0, sizeof(startInfo));
325
startInfo.cb = sizeof(startInfo);
327
bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),
329
CREATE_NO_WINDOW | CREATE_SUSPENDED,
330
0, 0, &startInfo, &procInfo);
333
CloseHandle(procInfo.hThread);
334
TerminateProcess(procInfo.hProcess, 0);
335
CloseHandle(procInfo.hProcess);
341
Creates a commandling from \a program and it \a arguments,
342
escaping characters that needs it.
344
static QString qt_create_commandline(const QString &program, const QStringList &arguments)
346
QString programName = program;
347
if (!programName.startsWith("\"") && !programName.endsWith("\"") && programName.contains(" "))
348
programName = "\"" + programName + "\"";
349
programName.replace("/", "\\");
352
// add the prgram as the first arrg ... it works better
353
args = programName + " ";
354
for (int i=0; i<arguments.size(); ++i) {
355
QString tmp = arguments.at(i);
356
// in the case of \" already being in the string the \ must also be escaped
357
tmp.replace( "\\\"", "\\\\\"" );
358
// escape a single " because the arguments will be parsed
359
tmp.replace( "\"", "\\\"" );
360
if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\t')) {
361
// The argument must not end with a \ since this would be interpreted
362
// as escaping the quote -- rather put the \ behind the quote: e.g.
363
// rather use "foo"\ than "foo\"
364
QString endQuote("\"");
365
int i = tmp.length();
366
while (i>0 && tmp.at(i-1) == '\\') {
370
args += QString(" \"") + tmp.left(i) + endQuote;
379
Creates a QByteArray of the \a environment.
381
static QByteArray qt_create_environment(const QStringList &environment)
384
if (environment.isEmpty())
388
// add PATH if necessary (for DLL loading)
389
QByteArray path = qgetenv("PATH");
390
if (environment.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty() && !path.isNull()) {
391
QString tmp = QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path));
392
uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
393
envlist.resize(envlist.size() + tmpSize);
394
memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
397
// add the user environment
398
for (QStringList::ConstIterator it = environment.begin(); it != environment.end(); it++ ) {
400
uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
401
envlist.resize(envlist.size() + tmpSize);
402
memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
405
// add the 2 terminating 0 (actually 4, just to be on the safe side)
406
envlist.resize(envlist.size() + 4);
416
Executes the command described in \a arguments, in the
417
environment inherited from the parent process, with the
418
\a additionalEnv settings applied.
419
\a removeEnv removes the specified environment variables from
420
the environment of the executed process.
422
Returns the exit value of the process, or -1 if the command could
425
This function uses _(w)spawnvpe to spawn a process by searching
426
through the PATH environment variable.
428
int Environment::execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv)
430
#ifdef CONFIGURE_DEBUG_EXECUTE
431
qDebug() << "About to Execute: " << arguments;
432
qDebug() << " " << QDir::currentPath();
433
qDebug() << " " << additionalEnv;
434
qDebug() << " " << removeEnv;
436
// Create the full environment from the current environment and
437
// the additionalEnv strings, then remove all variables defined
439
QMap<QString, QString> fullEnvMap;
440
LPWSTR envStrings = GetEnvironmentStrings();
443
for (LPWSTR envString = envStrings; *(envString); envString += strLen + 1) {
444
strLen = wcslen(envString);
445
QString str = QString((const QChar*)envString, strLen);
446
if (!str.startsWith("=")) { // These are added by the system
447
int sepIndex = str.indexOf('=');
448
fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
452
FreeEnvironmentStrings(envStrings);
454
// Add additionalEnv variables
455
for (int i = 0; i < additionalEnv.count(); ++i) {
456
const QString &str = additionalEnv.at(i);
457
int sepIndex = str.indexOf('=');
458
fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
461
// Remove removeEnv variables
462
for (int j = 0; j < removeEnv.count(); ++j)
463
fullEnvMap.remove(removeEnv.at(j).toUpper());
465
// Add all variables to a QStringList
467
QMapIterator<QString, QString> it(fullEnvMap);
468
while (it.hasNext()) {
470
fullEnv += QString(it.key() + "=" + it.value());
473
// ----------------------------
474
QString program = arguments.takeAt(0);
475
QString args = qt_create_commandline(program, arguments);
476
QByteArray envlist = qt_create_environment(fullEnv);
479
PROCESS_INFORMATION procInfo;
480
memset(&procInfo, 0, sizeof(procInfo));
482
STARTUPINFO startInfo;
483
memset(&startInfo, 0, sizeof(startInfo));
484
startInfo.cb = sizeof(startInfo);
486
bool couldExecute = CreateProcess(0, (wchar_t*)args.utf16(),
487
0, 0, true, CREATE_UNICODE_ENVIRONMENT,
488
envlist.isEmpty() ? 0 : envlist.data(),
489
0, &startInfo, &procInfo);
492
WaitForSingleObject(procInfo.hProcess, INFINITE);
493
GetExitCodeProcess(procInfo.hProcess, &exitCode);
494
CloseHandle(procInfo.hThread);
495
CloseHandle(procInfo.hProcess);
499
if (exitCode == -1) {
500
switch(GetLastError()) {
502
cerr << "execute: Argument list exceeds 1024 bytes" << endl;
503
foreach(QString arg, arguments)
504
cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;
507
cerr << "execute: File or path is not found (" << program.toLocal8Bit().constData() << ")" << endl;
510
cerr << "execute: Specified file is not executable or has invalid executable-file format (" << program.toLocal8Bit().constData() << ")" << endl;
513
cerr << "execute: Not enough memory is available to execute new process." << endl;
516
cerr << "execute: Unknown error" << endl;
517
foreach(QString arg, arguments)
518
cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;
525
bool Environment::cpdir(const QString &srcDir, const QString &destDir)
527
QString cleanSrcName = QDir::cleanPath(srcDir);
528
QString cleanDstName = QDir::cleanPath(destDir);
529
#ifdef CONFIGURE_DEBUG_CP_DIR
530
qDebug() << "Attempt to cpdir " << cleanSrcName << "->" << cleanDstName;
532
if(!QFile::exists(cleanDstName) && !QDir().mkpath(cleanDstName)) {
533
qDebug() << "cpdir: Failure to create " << cleanDstName;
538
QDir dir = QDir(cleanSrcName);
539
QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
540
for (int i = 0; result && (i < allEntries.count()); ++i) {
541
QFileInfo entry = allEntries.at(i);
542
bool intermediate = true;
544
intermediate = cpdir(QString("%1/%2").arg(cleanSrcName).arg(entry.fileName()),
545
QString("%1/%2").arg(cleanDstName).arg(entry.fileName()));
547
QString destFile = QString("%1/%2").arg(cleanDstName).arg(entry.fileName());
548
#ifdef CONFIGURE_DEBUG_CP_DIR
549
qDebug() << "About to cp (file)" << entry.absoluteFilePath() << "->" << destFile;
551
QFile::remove(destFile);
552
intermediate = QFile::copy(entry.absoluteFilePath(), destFile);
553
SetFileAttributes((wchar_t*)destFile.utf16(), FILE_ATTRIBUTE_NORMAL);
556
qDebug() << "cpdir: Failure for " << entry.fileName() << entry.isDir();
563
bool Environment::rmdir(const QString &name)
566
QString cleanName = QDir::cleanPath(name);
568
QDir dir = QDir(cleanName);
569
QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
570
for (int i = 0; result && (i < allEntries.count()); ++i) {
571
QFileInfo entry = allEntries.at(i);
573
result &= rmdir(entry.absoluteFilePath());
575
result &= QFile::remove(entry.absoluteFilePath());
578
result &= dir.rmdir(cleanName);