source: trunk/tools/configure/environment.cpp@ 769

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 16.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "environment.h"
43
44#include <process.h>
45#include <iostream>
46#include <qdebug.h>
47#include <QDir>
48#include <QStringList>
49#include <QMap>
50#include <QDir>
51#include <QFile>
52#include <QFileInfo>
53
54//#define CONFIGURE_DEBUG_EXECUTE
55//#define CONFIGURE_DEBUG_CP_DIR
56
57using namespace std;
58
59#ifdef Q_OS_WIN32
60#include <qt_windows.h>
61#endif
62
63#include <symbian/epocroot.h> // from tools/shared
64#include <windows/registry.h> // from tools/shared
65
66QT_BEGIN_NAMESPACE
67
68struct CompilerInfo{
69 Compiler compiler;
70 const char *compilerStr;
71 const char *regKey;
72 const char *executable;
73} compiler_info[] = {
74 // The compilers here are sorted in a reversed-preferred order
75 {CC_BORLAND, "Borland C++", 0, "bcc32.exe"},
76 {CC_MINGW, "MinGW (Minimalist GNU for Windows)", 0, "mingw32-gcc.exe"},
77 {CC_INTEL, "Intel(R) C++ Compiler for 32-bit applications", 0, "icl.exe"}, // xilink.exe, xilink5.exe, xilink6.exe, xilib.exe
78 {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
79 {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
80 {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
81 {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
82 {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
83 {CC_UNKNOWN, "Unknown", 0, 0},
84};
85
86
87// Initialize static variables
88Compiler Environment::detectedCompiler = CC_UNKNOWN;
89
90/*!
91 Returns the pointer to the CompilerInfo for a \a compiler.
92*/
93CompilerInfo *Environment::compilerInfo(Compiler compiler)
94{
95 int i = 0;
96 while(compiler_info[i].compiler != compiler && compiler_info[i].compiler != CC_UNKNOWN)
97 ++i;
98 return &(compiler_info[i]);
99}
100
101/*!
102 Returns the qmakespec for the compiler detected on the system.
103*/
104QString Environment::detectQMakeSpec()
105{
106 QString spec;
107 switch (detectCompiler()) {
108 case CC_NET2008:
109 spec = "win32-msvc2008";
110 break;
111 case CC_NET2005:
112 spec = "win32-msvc2005";
113 break;
114 case CC_NET2003:
115 spec = "win32-msvc2003";
116 break;
117 case CC_NET2002:
118 spec = "win32-msvc2002";
119 break;
120 case CC_MSVC4:
121 case CC_MSVC5:
122 case CC_MSVC6:
123 spec = "win32-msvc";
124 break;
125 case CC_INTEL:
126 spec = "win32-icc";
127 break;
128 case CC_MINGW:
129 spec = "win32-g++";
130 break;
131 case CC_BORLAND:
132 spec = "win32-borland";
133 break;
134 default:
135 break;
136 }
137
138 return spec;
139}
140
141/*!
142 Returns the enum of the compiler which was detected on the system.
143 The compilers are detected in the order as entered into the
144 compiler_info list.
145
146 If more than one compiler is found, CC_UNKNOWN is returned.
147*/
148Compiler Environment::detectCompiler()
149{
150#ifndef Q_OS_WIN32
151 return MSVC6; // Always generate MSVC 6.0 versions on other platforms
152#else
153 if(detectedCompiler != CC_UNKNOWN)
154 return detectedCompiler;
155
156 int installed = 0;
157
158 // Check for compilers in registry first, to see which version is in PATH
159 QString paths = qgetenv("PATH");
160 QStringList pathlist = paths.toLower().split(";");
161 for(int i = 0; compiler_info[i].compiler; ++i) {
162 QString productPath = readRegistryKey(HKEY_LOCAL_MACHINE, compiler_info[i].regKey).toLower();
163 if (productPath.length()) {
164 QStringList::iterator it;
165 for(it = pathlist.begin(); it != pathlist.end(); ++it) {
166 if((*it).contains(productPath)) {
167 ++installed;
168 detectedCompiler = compiler_info[i].compiler;
169 break;
170 }
171 }
172 }
173 }
174
175 // Now just go looking for the executables, and accept any executable as the lowest version
176 if (!installed) {
177 for(int i = 0; compiler_info[i].compiler; ++i) {
178 QString executable = QString(compiler_info[i].executable).toLower();
179 if (executable.length() && Environment::detectExecutable(executable)) {
180 ++installed;
181 detectedCompiler = compiler_info[i].compiler;
182 break;
183 }
184 }
185 }
186
187 if (installed > 1) {
188 cout << "Found more than one known compiler! Using \"" << compilerInfo(detectedCompiler)->compilerStr << "\"" << endl;
189 detectedCompiler = CC_UNKNOWN;
190 }
191 return detectedCompiler;
192#endif
193};
194
195/*!
196 Returns true if the \a executable could be loaded, else false.
197 This means that the executable either is in the current directory
198 or in the PATH.
199*/
200bool Environment::detectExecutable(const QString &executable)
201{
202 PROCESS_INFORMATION procInfo;
203 memset(&procInfo, 0, sizeof(procInfo));
204
205 STARTUPINFO startInfo;
206 memset(&startInfo, 0, sizeof(startInfo));
207 startInfo.cb = sizeof(startInfo);
208
209 bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),
210 0, 0, false,
211 CREATE_NO_WINDOW | CREATE_SUSPENDED,
212 0, 0, &startInfo, &procInfo);
213
214 if (couldExecute) {
215 CloseHandle(procInfo.hThread);
216 TerminateProcess(procInfo.hProcess, 0);
217 CloseHandle(procInfo.hProcess);
218 }
219 return couldExecute;
220}
221
222/*!
223 Creates a commandling from \a program and it \a arguments,
224 escaping characters that needs it.
225*/
226static QString qt_create_commandline(const QString &program, const QStringList &arguments)
227{
228 QString programName = program;
229 if (!programName.startsWith("\"") && !programName.endsWith("\"") && programName.contains(" "))
230 programName = "\"" + programName + "\"";
231 programName.replace("/", "\\");
232
233 QString args;
234 // add the prgram as the first arrg ... it works better
235 args = programName + " ";
236 for (int i=0; i<arguments.size(); ++i) {
237 QString tmp = arguments.at(i);
238 // in the case of \" already being in the string the \ must also be escaped
239 tmp.replace( "\\\"", "\\\\\"" );
240 // escape a single " because the arguments will be parsed
241 tmp.replace( "\"", "\\\"" );
242 if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\t')) {
243 // The argument must not end with a \ since this would be interpreted
244 // as escaping the quote -- rather put the \ behind the quote: e.g.
245 // rather use "foo"\ than "foo\"
246 QString endQuote("\"");
247 int i = tmp.length();
248 while (i>0 && tmp.at(i-1) == '\\') {
249 --i;
250 endQuote += "\\";
251 }
252 args += QString(" \"") + tmp.left(i) + endQuote;
253 } else {
254 args += ' ' + tmp;
255 }
256 }
257 return args;
258}
259
260/*!
261 Creates a QByteArray of the \a environment.
262*/
263static QByteArray qt_create_environment(const QStringList &environment)
264{
265 QByteArray envlist;
266 if (environment.isEmpty())
267 return envlist;
268
269 int pos = 0;
270 // add PATH if necessary (for DLL loading)
271 QByteArray path = qgetenv("PATH");
272 if (environment.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty() && !path.isNull()) {
273 QString tmp = QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path));
274 uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
275 envlist.resize(envlist.size() + tmpSize);
276 memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
277 pos += tmpSize;
278 }
279 // add the user environment
280 for (QStringList::ConstIterator it = environment.begin(); it != environment.end(); it++ ) {
281 QString tmp = *it;
282 uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
283 envlist.resize(envlist.size() + tmpSize);
284 memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
285 pos += tmpSize;
286 }
287 // add the 2 terminating 0 (actually 4, just to be on the safe side)
288 envlist.resize(envlist.size() + 4);
289 envlist[pos++] = 0;
290 envlist[pos++] = 0;
291 envlist[pos++] = 0;
292 envlist[pos++] = 0;
293
294 return envlist;
295}
296
297/*!
298 Executes the command described in \a arguments, in the
299 environment inherited from the parent process, with the
300 \a additionalEnv settings applied.
301 \a removeEnv removes the specified environment variables from
302 the environment of the executed process.
303
304 Returns the exit value of the process, or -1 if the command could
305 not be executed.
306
307 This function uses _(w)spawnvpe to spawn a process by searching
308 through the PATH environment variable.
309*/
310int Environment::execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv)
311{
312#ifdef CONFIGURE_DEBUG_EXECUTE
313 qDebug() << "About to Execute: " << arguments;
314 qDebug() << " " << QDir::currentPath();
315 qDebug() << " " << additionalEnv;
316 qDebug() << " " << removeEnv;
317#endif
318 // Create the full environment from the current environment and
319 // the additionalEnv strings, then remove all variables defined
320 // in removeEnv
321 QMap<QString, QString> fullEnvMap;
322 LPWSTR envStrings = GetEnvironmentStrings();
323 if (envStrings) {
324 int strLen = 0;
325 for (LPWSTR envString = envStrings; *(envString); envString += strLen + 1) {
326 strLen = wcslen(envString);
327 QString str = QString((const QChar*)envString, strLen);
328 if (!str.startsWith("=")) { // These are added by the system
329 int sepIndex = str.indexOf('=');
330 fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
331 }
332 }
333 }
334 FreeEnvironmentStrings(envStrings);
335
336 // Add additionalEnv variables
337 for (int i = 0; i < additionalEnv.count(); ++i) {
338 const QString &str = additionalEnv.at(i);
339 int sepIndex = str.indexOf('=');
340 fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
341 }
342
343 // Remove removeEnv variables
344 for (int j = 0; j < removeEnv.count(); ++j)
345 fullEnvMap.remove(removeEnv.at(j).toUpper());
346
347 // Add all variables to a QStringList
348 QStringList fullEnv;
349 QMapIterator<QString, QString> it(fullEnvMap);
350 while (it.hasNext()) {
351 it.next();
352 fullEnv += QString(it.key() + "=" + it.value());
353 }
354
355 // ----------------------------
356 QString program = arguments.takeAt(0);
357 QString args = qt_create_commandline(program, arguments);
358 QByteArray envlist = qt_create_environment(fullEnv);
359
360 DWORD exitCode = -1;
361 PROCESS_INFORMATION procInfo;
362 memset(&procInfo, 0, sizeof(procInfo));
363
364 STARTUPINFO startInfo;
365 memset(&startInfo, 0, sizeof(startInfo));
366 startInfo.cb = sizeof(startInfo);
367
368 bool couldExecute = CreateProcess(0, (wchar_t*)args.utf16(),
369 0, 0, true, CREATE_UNICODE_ENVIRONMENT,
370 envlist.isEmpty() ? 0 : envlist.data(),
371 0, &startInfo, &procInfo);
372
373 if (couldExecute) {
374 WaitForSingleObject(procInfo.hProcess, INFINITE);
375 GetExitCodeProcess(procInfo.hProcess, &exitCode);
376 CloseHandle(procInfo.hThread);
377 CloseHandle(procInfo.hProcess);
378 }
379
380
381 if (exitCode == -1) {
382 switch(GetLastError()) {
383 case E2BIG:
384 cerr << "execute: Argument list exceeds 1024 bytes" << endl;
385 foreach(QString arg, arguments)
386 cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;
387 break;
388 case ENOENT:
389 cerr << "execute: File or path is not found (" << program.toLocal8Bit().constData() << ")" << endl;
390 break;
391 case ENOEXEC:
392 cerr << "execute: Specified file is not executable or has invalid executable-file format (" << program.toLocal8Bit().constData() << ")" << endl;
393 break;
394 case ENOMEM:
395 cerr << "execute: Not enough memory is available to execute new process." << endl;
396 break;
397 default:
398 cerr << "execute: Unknown error" << endl;
399 foreach(QString arg, arguments)
400 cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;
401 break;
402 }
403 }
404 return exitCode;
405}
406
407bool Environment::cpdir(const QString &srcDir, const QString &destDir)
408{
409 QString cleanSrcName = QDir::cleanPath(srcDir);
410 QString cleanDstName = QDir::cleanPath(destDir);
411#ifdef CONFIGURE_DEBUG_CP_DIR
412 qDebug() << "Attempt to cpdir " << cleanSrcName << "->" << cleanDstName;
413#endif
414 if(!QFile::exists(cleanDstName) && !QDir().mkpath(cleanDstName)) {
415 qDebug() << "cpdir: Failure to create " << cleanDstName;
416 return false;
417 }
418
419 bool result = true;
420 QDir dir = QDir(cleanSrcName);
421 QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
422 for (int i = 0; result && (i < allEntries.count()); ++i) {
423 QFileInfo entry = allEntries.at(i);
424 bool intermediate = true;
425 if (entry.isDir()) {
426 intermediate = cpdir(QString("%1/%2").arg(cleanSrcName).arg(entry.fileName()),
427 QString("%1/%2").arg(cleanDstName).arg(entry.fileName()));
428 } else {
429 QString destFile = QString("%1/%2").arg(cleanDstName).arg(entry.fileName());
430#ifdef CONFIGURE_DEBUG_CP_DIR
431 qDebug() << "About to cp (file)" << entry.absoluteFilePath() << "->" << destFile;
432#endif
433 QFile::remove(destFile);
434 intermediate = QFile::copy(entry.absoluteFilePath(), destFile);
435 SetFileAttributes((wchar_t*)destFile.utf16(), FILE_ATTRIBUTE_NORMAL);
436 }
437 if(!intermediate) {
438 qDebug() << "cpdir: Failure for " << entry.fileName() << entry.isDir();
439 result = false;
440 }
441 }
442 return result;
443}
444
445bool Environment::rmdir(const QString &name)
446{
447 bool result = true;
448 QString cleanName = QDir::cleanPath(name);
449
450 QDir dir = QDir(cleanName);
451 QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
452 for (int i = 0; result && (i < allEntries.count()); ++i) {
453 QFileInfo entry = allEntries.at(i);
454 if (entry.isDir()) {
455 result &= rmdir(entry.absoluteFilePath());
456 } else {
457 result &= QFile::remove(entry.absoluteFilePath());
458 }
459 }
460 result &= dir.rmdir(cleanName);
461 return result;
462}
463
464QString Environment::symbianEpocRoot()
465{
466 // Call function defined in tools/shared/symbian/epocroot.h
467 return ::epocRoot();
468}
469
470QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.