1 /****************************************************************************
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the tools applications of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "environment.h"
49 #include <QStringList>
55 //#define CONFIGURE_DEBUG_EXECUTE
56 //#define CONFIGURE_DEBUG_CP_DIR
61 #include <qt_windows.h>
64 #include <symbian/epocroot_p.h> // from tools/shared
65 #include <windows/registry_p.h> // from tools/shared
71 const char *compilerStr;
73 const char *executable;
75 // The compilers here are sorted in a reversed-preferred order
76 {CC_BORLAND, "Borland C++", 0, "bcc32.exe"},
77 {CC_MINGW, "MinGW (Minimalist GNU for Windows)", 0, "g++.exe"},
78 {CC_INTEL, "Intel(R) C++ Compiler for 32-bit applications", 0, "icl.exe"}, // xilink.exe, xilink5.exe, xilink6.exe, xilib.exe
79 {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
80 {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
81 {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
82 {CC_NET2010, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2010 (10.0)", "Software\\Microsoft\\VisualStudio\\SxS\\VC7\\10.0", "cl.exe"}, // link.exe, lib.exe
83 {CC_NET2012, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2012 (11.0)", "Software\\Microsoft\\VisualStudio\\SxS\\VC7\\11.0", "cl.exe"}, // link.exe, lib.exe
84 {CC_NET2012, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2012 (11.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\11.0", "cl.exe"}, // link.exe, lib.exe
85 {CC_NET2013, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2013 (12.0)", "Software\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0", "cl.exe"}, // link.exe, lib.exe
86 {CC_NET2013, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2013 (12.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\12.0", "cl.exe"}, // link.exe, lib.exe
87 {CC_UNKNOWN, "Unknown", 0, 0},
91 // Initialize static variables
92 Compiler Environment::detectedCompiler = CC_UNKNOWN;
95 Returns the pointer to the CompilerInfo for a \a compiler.
97 CompilerInfo *Environment::compilerInfo(Compiler compiler)
100 while(compiler_info[i].compiler != compiler && compiler_info[i].compiler != CC_UNKNOWN)
102 return &(compiler_info[i]);
106 Returns the qmakespec for the compiler detected on the system.
108 QString Environment::detectQMakeSpec()
111 switch (detectCompiler()) {
113 spec = "win32-msvc2013";
116 spec = "win32-msvc2012";
119 spec = "win32-msvc2010";
122 spec = "win32-msvc2008";
125 spec = "win32-msvc2005";
128 spec = "win32-msvc2003";
134 spec = "win32-g++-4.6";
140 spec = "win32-borland";
150 Returns the enum of the compiler which was detected on the system.
151 The compilers are detected in the order as entered into the
154 If more than one compiler is found, CC_UNKNOWN is returned.
156 Compiler Environment::detectCompiler()
159 return CC_UNKNOWN; // Always generate CC_UNKNOWN on other platforms
161 if(detectedCompiler != CC_UNKNOWN)
162 return detectedCompiler;
166 // Check for compilers in registry first, to see which version is in PATH
167 QString paths = qgetenv("PATH");
168 QStringList pathlist = paths.toLower().split(";");
169 for(int i = 0; compiler_info[i].compiler; ++i) {
170 QString productPath = qt_readRegistryKey(HKEY_LOCAL_MACHINE, compiler_info[i].regKey).toLower();
171 if (productPath.length()) {
172 QStringList::iterator it;
173 for(it = pathlist.begin(); it != pathlist.end(); ++it) {
174 if((*it).contains(productPath)) {
175 if (detectedCompiler != compiler_info[i].compiler) {
177 detectedCompiler = compiler_info[i].compiler;
181 We detected the same compiler again, which happens when
182 configure is run on a 64 bit Windows. Skip the
183 duplicate so that we don't think it's installed twice.
193 // Now just go looking for the executables, and accept any executable as the lowest version
195 for(int i = 0; compiler_info[i].compiler; ++i) {
196 QString executable = QString(compiler_info[i].executable).toLower();
197 if (executable.length() && Environment::detectExecutable(executable)) {
198 if (detectedCompiler != compiler_info[i].compiler) {
200 detectedCompiler = compiler_info[i].compiler;
201 if (detectedCompiler == CC_MINGW) {
202 const int version = detectGPlusPlusVersion(executable);
203 if (version < 0x040600)
204 detectedCompiler = CC_MINGW_44;
209 We detected the same compiler again, which happens when
210 configure is run on a 64 bit Windows. Skip the
211 duplicate so that we don't think it's installed twice.
221 cout << "Found more than one known compiler! Using \"" << compilerInfo(detectedCompiler)->compilerStr << "\"" << endl;
222 detectedCompiler = CC_UNKNOWN;
224 return detectedCompiler;
229 Returns true if the \a executable could be loaded, else false.
230 This means that the executable either is in the current directory
233 bool Environment::detectExecutable(const QString &executable)
235 PROCESS_INFORMATION procInfo;
236 memset(&procInfo, 0, sizeof(procInfo));
238 STARTUPINFO startInfo;
239 memset(&startInfo, 0, sizeof(startInfo));
240 startInfo.cb = sizeof(startInfo);
242 bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),
244 CREATE_NO_WINDOW | CREATE_SUSPENDED,
245 0, 0, &startInfo, &procInfo);
248 CloseHandle(procInfo.hThread);
249 TerminateProcess(procInfo.hProcess, 0);
250 CloseHandle(procInfo.hProcess);
256 Determine the g++ version.
259 int Environment::detectGPlusPlusVersion(const QString &executable)
261 QRegExp regexp(QLatin1String("[gG]\\+\\+[\\.exEX]{0,4} ([^\\n]+) (\\d+)\\.(\\d+)\\.(\\d+)"));
262 QString stdOut = readProcessStandardOutput(executable + QLatin1String(" --version"));
263 if (regexp.indexIn(stdOut) != -1) {
264 const QString compiler = regexp.cap(1);
265 const int major = regexp.cap(2).toInt();
266 const int minor = regexp.cap(3).toInt();
267 const int patch = regexp.cap(4).toInt();
268 return (major << 16) + (minor << 8) + patch;
274 Run a process and return its standard output.
277 QString Environment::readProcessStandardOutput(const QString &commandLine)
280 TCHAR tempFileName[MAX_PATH];
281 TCHAR tempPathBuffer[MAX_PATH];
282 if (!GetTempPath(MAX_PATH, tempPathBuffer)
283 || !GetTempFileName(tempPathBuffer, TEXT("qtconfigure"), 0, tempFileName))
286 STARTUPINFO startInfo;
287 memset(&startInfo, 0, sizeof(startInfo));
288 startInfo.cb = sizeof(startInfo);
289 startInfo.dwFlags |= STARTF_USESTDHANDLES;
291 SECURITY_ATTRIBUTES securityAttributes;
292 securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
293 securityAttributes.bInheritHandle = TRUE;
294 securityAttributes.lpSecurityDescriptor = NULL;
296 startInfo.hStdOutput = CreateFile(tempFileName, GENERIC_WRITE, 0, &securityAttributes, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
297 if (startInfo.hStdOutput == INVALID_HANDLE_VALUE)
300 PROCESS_INFORMATION procInfo;
301 memset(&procInfo, 0, sizeof(procInfo));
303 if (!CreateProcess(0, (wchar_t*)commandLine.utf16(),
306 0, 0, &startInfo, &procInfo)) {
307 CloseHandle(startInfo.hStdOutput);
308 DeleteFile(tempFileName);
312 WaitForSingleObject(procInfo.hProcess, INFINITE);
313 CloseHandle(procInfo.hThread);
314 CloseHandle(procInfo.hProcess);
315 CloseHandle(startInfo.hStdOutput);
316 QFile file(QString::fromWCharArray(tempFileName));
318 if (file.open(QIODevice::Text| QIODevice::ReadOnly)) {
319 stdOut = QString::fromLocal8Bit(file.readAll());
322 DeleteFile(tempFileName);
327 Creates a commandling from \a program and it \a arguments,
328 escaping characters that needs it.
330 static QString qt_create_commandline(const QString &program, const QStringList &arguments)
332 QString programName = program;
333 if (!programName.startsWith("\"") && !programName.endsWith("\"") && programName.contains(" "))
334 programName = "\"" + programName + "\"";
335 programName.replace("/", "\\");
338 // add the prgram as the first arrg ... it works better
339 args = programName + " ";
340 for (int i=0; i<arguments.size(); ++i) {
341 QString tmp = arguments.at(i);
342 // in the case of \" already being in the string the \ must also be escaped
343 tmp.replace( "\\\"", "\\\\\"" );
344 // escape a single " because the arguments will be parsed
345 tmp.replace( "\"", "\\\"" );
346 if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\t')) {
347 // The argument must not end with a \ since this would be interpreted
348 // as escaping the quote -- rather put the \ behind the quote: e.g.
349 // rather use "foo"\ than "foo\"
350 QString endQuote("\"");
351 int i = tmp.length();
352 while (i>0 && tmp.at(i-1) == '\\') {
356 args += QString(" \"") + tmp.left(i) + endQuote;
365 Creates a QByteArray of the \a environment.
367 static QByteArray qt_create_environment(const QStringList &environment)
370 if (environment.isEmpty())
374 // add PATH if necessary (for DLL loading)
375 QByteArray path = qgetenv("PATH");
376 if (environment.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty() && !path.isNull()) {
377 QString tmp = QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path));
378 uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
379 envlist.resize(envlist.size() + tmpSize);
380 memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
383 // add the user environment
384 foreach (const QString &tmp, environment) {
385 uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
386 envlist.resize(envlist.size() + tmpSize);
387 memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
390 // add the 2 terminating 0 (actually 4, just to be on the safe side)
391 envlist.resize(envlist.size() + 4);
401 Executes the command described in \a arguments, in the
402 environment inherited from the parent process, with the
403 \a additionalEnv settings applied.
404 \a removeEnv removes the specified environment variables from
405 the environment of the executed process.
407 Returns the exit value of the process, or -1 if the command could
410 This function uses _(w)spawnvpe to spawn a process by searching
411 through the PATH environment variable.
413 int Environment::execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv)
415 #ifdef CONFIGURE_DEBUG_EXECUTE
416 qDebug() << "About to Execute: " << arguments;
417 qDebug() << " " << QDir::currentPath();
418 qDebug() << " " << additionalEnv;
419 qDebug() << " " << removeEnv;
421 // Create the full environment from the current environment and
422 // the additionalEnv strings, then remove all variables defined
424 QMap<QString, QString> fullEnvMap;
425 LPWSTR envStrings = GetEnvironmentStrings();
428 for (LPWSTR envString = envStrings; *(envString); envString += strLen + 1) {
429 strLen = wcslen(envString);
430 QString str = QString((const QChar*)envString, strLen);
431 if (!str.startsWith("=")) { // These are added by the system
432 int sepIndex = str.indexOf('=');
433 fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
437 FreeEnvironmentStrings(envStrings);
439 // Add additionalEnv variables
440 for (int i = 0; i < additionalEnv.count(); ++i) {
441 const QString &str = additionalEnv.at(i);
442 int sepIndex = str.indexOf('=');
443 fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
446 // Remove removeEnv variables
447 for (int j = 0; j < removeEnv.count(); ++j)
448 fullEnvMap.remove(removeEnv.at(j).toUpper());
450 // Add all variables to a QStringList
452 QMapIterator<QString, QString> it(fullEnvMap);
453 while (it.hasNext()) {
455 fullEnv += QString(it.key() + "=" + it.value());
458 // ----------------------------
459 QString program = arguments.takeAt(0);
460 QString args = qt_create_commandline(program, arguments);
461 QByteArray envlist = qt_create_environment(fullEnv);
463 DWORD exitCode = DWORD(-1);
464 PROCESS_INFORMATION procInfo;
465 memset(&procInfo, 0, sizeof(procInfo));
467 STARTUPINFO startInfo;
468 memset(&startInfo, 0, sizeof(startInfo));
469 startInfo.cb = sizeof(startInfo);
471 bool couldExecute = CreateProcess(0, (wchar_t*)args.utf16(),
472 0, 0, true, CREATE_UNICODE_ENVIRONMENT,
473 envlist.isEmpty() ? 0 : envlist.data(),
474 0, &startInfo, &procInfo);
477 WaitForSingleObject(procInfo.hProcess, INFINITE);
478 GetExitCodeProcess(procInfo.hProcess, &exitCode);
479 CloseHandle(procInfo.hThread);
480 CloseHandle(procInfo.hProcess);
484 if (exitCode == DWORD(-1)) {
485 switch(GetLastError()) {
487 cerr << "execute: Argument list exceeds 1024 bytes" << endl;
488 foreach (const QString &arg, arguments)
489 cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;
492 cerr << "execute: File or path is not found (" << program.toLocal8Bit().constData() << ")" << endl;
495 cerr << "execute: Specified file is not executable or has invalid executable-file format (" << program.toLocal8Bit().constData() << ")" << endl;
498 cerr << "execute: Not enough memory is available to execute new process." << endl;
501 cerr << "execute: Unknown error" << endl;
502 foreach (const QString &arg, arguments)
503 cerr << " (" << arg.toLocal8Bit().constData() << ")" << endl;
511 Copies the \a srcDir contents into \a destDir.
513 If \a includeSrcDir is not empty, any files with 'h', 'prf', or 'conf' suffixes
514 will not be copied over from \a srcDir. Instead a new file will be created
515 in \a destDir with the same name and that file will include a file with the
516 same name from the \a includeSrcDir using relative path and appropriate
517 syntax for the file type.
519 Returns true if copying was successful.
521 bool Environment::cpdir(const QString &srcDir,
522 const QString &destDir,
523 const QString &includeSrcDir)
525 QString cleanSrcName = QDir::cleanPath(srcDir);
526 QString cleanDstName = QDir::cleanPath(destDir);
527 QString cleanIncludeName = QDir::cleanPath(includeSrcDir);
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 QDir destinationDir = QDir(cleanDstName);
540 QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
541 for (int i = 0; result && (i < allEntries.count()); ++i) {
542 QFileInfo entry = allEntries.at(i);
543 bool intermediate = true;
545 QString newIncSrcDir;
546 if (!includeSrcDir.isEmpty())
547 newIncSrcDir = QString("%1/%2").arg(cleanIncludeName).arg(entry.fileName());
549 intermediate = cpdir(QString("%1/%2").arg(cleanSrcName).arg(entry.fileName()),
550 QString("%1/%2").arg(cleanDstName).arg(entry.fileName()),
553 QString destFile = QString("%1/%2").arg(cleanDstName).arg(entry.fileName());
554 #ifdef CONFIGURE_DEBUG_CP_DIR
555 qDebug() << "About to cp (file)" << entry.absoluteFilePath() << "->" << destFile;
557 QFile::remove(destFile);
558 QString suffix = entry.suffix();
559 if (!includeSrcDir.isEmpty() && (suffix == "prf" || suffix == "conf" || suffix == "h")) {
560 QString relativeIncludeFilePath = QString("%1/%2").arg(cleanIncludeName).arg(entry.fileName());
561 relativeIncludeFilePath = destinationDir.relativeFilePath(relativeIncludeFilePath);
562 #ifdef CONFIGURE_DEBUG_CP_DIR
563 qDebug() << "...instead generate relative include to" << relativeIncludeFilePath;
565 QFile currentFile(destFile);
566 if (currentFile.open(QFile::WriteOnly | QFile::Text)) {
567 QTextStream fileStream;
568 fileStream.setDevice(¤tFile);
570 if (suffix == "prf" || suffix == "conf") {
571 if (entry.fileName() == "qmake.conf") {
572 // While QMAKESPEC_ORIGINAL being relative or absolute doesn't matter for the
573 // primary use of this variable by qmake to identify the original mkspec, the
574 // variable is also used for few special cases where the absolute path is required.
575 // Conversely, the include of the original qmake.conf must be done using relative path,
576 // as some Qt binary deployments are done in a manner that doesn't allow for patching
577 // the paths at the installation time.
578 fileStream << "QMAKESPEC_ORIGINAL=" << cleanSrcName << endl << endl;
580 fileStream << "include(" << relativeIncludeFilePath << ")" << endl << endl;
581 } else if (suffix == "h") {
582 fileStream << "#include \"" << relativeIncludeFilePath << "\"" << endl << endl;
589 intermediate = QFile::copy(entry.absoluteFilePath(), destFile);
590 SetFileAttributes((wchar_t*)destFile.utf16(), FILE_ATTRIBUTE_NORMAL);
594 qDebug() << "cpdir: Failure for " << entry.fileName() << entry.isDir();
601 bool Environment::rmdir(const QString &name)
604 QString cleanName = QDir::cleanPath(name);
606 QDir dir = QDir(cleanName);
607 QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
608 for (int i = 0; result && (i < allEntries.count()); ++i) {
609 QFileInfo entry = allEntries.at(i);
611 result &= rmdir(entry.absoluteFilePath());
613 result &= QFile::remove(entry.absoluteFilePath());
616 result &= dir.rmdir(cleanName);
620 QString Environment::symbianEpocRoot()
622 // Call function defined in tools/shared/symbian/epocroot_p.h
623 return ::qt_epocRoot();