Add detection of and support for Visual Studio 2015
[qt:qt.git] / tools / configure / environment.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
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 The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "environment.h"
43
44 #include <process.h>
45 #include <errno.h>
46 #include <iostream>
47 #include <qdebug.h>
48 #include <QDir>
49 #include <QStringList>
50 #include <QMap>
51 #include <QDir>
52 #include <QFile>
53 #include <QFileInfo>
54
55 //#define CONFIGURE_DEBUG_EXECUTE
56 //#define CONFIGURE_DEBUG_CP_DIR
57
58 using namespace std;
59
60 #ifdef Q_OS_WIN32
61 #include <qt_windows.h>
62 #endif
63
64 #include <symbian/epocroot_p.h> // from tools/shared
65 #include <windows/registry_p.h> // from tools/shared
66
67 QT_BEGIN_NAMESPACE
68
69 struct CompilerInfo{
70     Compiler compiler;
71     const char *compilerStr;
72     const char *regKey;
73     const char *executable;
74 } compiler_info[] = {
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_NET2015, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2015 (14.0)", "Software\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0", "cl.exe"}, // link.exe, lib.exe
88     {CC_NET2015, "Microsoft (R) 32-bit C/C++ Optimizing Compiler.NET 2015 (14.0)", "Software\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\VC7\\14.0", "cl.exe"}, // link.exe, lib.exe
89     {CC_UNKNOWN, "Unknown", 0, 0},
90 };
91
92
93 // Initialize static variables
94 Compiler Environment::detectedCompiler = CC_UNKNOWN;
95
96 /*!
97     Returns the pointer to the CompilerInfo for a \a compiler.
98 */
99 CompilerInfo *Environment::compilerInfo(Compiler compiler)
100 {
101     int i = 0;
102     while(compiler_info[i].compiler != compiler && compiler_info[i].compiler != CC_UNKNOWN)
103         ++i;
104     return &(compiler_info[i]);
105 }
106
107 /*!
108     Returns the qmakespec for the compiler detected on the system.
109 */
110 QString Environment::detectQMakeSpec()
111 {
112     QString spec;
113     switch (detectCompiler()) {
114     case CC_NET2015:
115         spec = "win32-msvc2015";
116         break;
117     case CC_NET2013:
118         spec = "win32-msvc2013";
119         break;
120     case CC_NET2012:
121         spec = "win32-msvc2012";
122         break;
123     case CC_NET2010:
124         spec = "win32-msvc2010";
125         break;
126     case CC_NET2008:
127         spec = "win32-msvc2008";
128         break;
129     case CC_NET2005:
130         spec = "win32-msvc2005";
131         break;
132     case CC_NET2003:
133         spec = "win32-msvc2003";
134         break;
135     case CC_INTEL:
136         spec = "win32-icc";
137         break;
138     case CC_MINGW:
139         spec = "win32-g++-4.6";
140         break;
141     case CC_MINGW_44:
142         spec = "win32-g++";
143         break;
144     case CC_BORLAND:
145         spec = "win32-borland";
146         break;
147     default:
148         break;
149     }
150
151     return spec;
152 }
153
154 /*!
155     Returns the enum of the compiler which was detected on the system.
156     The compilers are detected in the order as entered into the
157     compiler_info list.
158
159     If more than one compiler is found, CC_UNKNOWN is returned.
160 */
161 Compiler Environment::detectCompiler()
162 {
163 #ifndef Q_OS_WIN32
164     return CC_UNKNOWN; // Always generate CC_UNKNOWN on other platforms
165 #else
166     if(detectedCompiler != CC_UNKNOWN)
167         return detectedCompiler;
168
169     int installed = 0;
170
171     // Check for compilers in registry first, to see which version is in PATH
172     QString paths = qgetenv("PATH");
173     QStringList pathlist = paths.toLower().split(";");
174     for(int i = 0; compiler_info[i].compiler; ++i) {
175         QString productPath = qt_readRegistryKey(HKEY_LOCAL_MACHINE, compiler_info[i].regKey).toLower();
176         if (productPath.length()) {
177             QStringList::iterator it;
178             for(it = pathlist.begin(); it != pathlist.end(); ++it) {
179                 if((*it).contains(productPath)) {
180                     if (detectedCompiler != compiler_info[i].compiler) {
181                         ++installed;
182                         detectedCompiler = compiler_info[i].compiler;
183                     }
184                     /* else {
185
186                         We detected the same compiler again, which happens when
187                         configure is run on a 64 bit Windows. Skip the
188                         duplicate so that we don't think it's installed twice.
189
190                     }
191                     */
192                     break;
193                 }
194             }
195         }
196     }
197
198     // Now just go looking for the executables, and accept any executable as the lowest version
199     if (!installed) {
200         for(int i = 0; compiler_info[i].compiler; ++i) {
201             QString executable = QString(compiler_info[i].executable).toLower();
202             if (executable.length() && Environment::detectExecutable(executable)) {
203                 if (detectedCompiler != compiler_info[i].compiler) {
204                     ++installed;
205                     detectedCompiler = compiler_info[i].compiler;
206                     if (detectedCompiler == CC_MINGW) {
207                         const int version = detectGPlusPlusVersion(executable);
208                         if (version < 0x040600)
209                             detectedCompiler = CC_MINGW_44;
210                     }
211                 }
212                 /* else {
213
214                     We detected the same compiler again, which happens when
215                     configure is run on a 64 bit Windows. Skip the
216                     duplicate so that we don't think it's installed twice.
217
218                 }
219                 */
220                 break;
221             }
222         }
223     }
224
225     if (installed > 1) {
226         cout << "Found more than one known compiler! Using \"" << compilerInfo(detectedCompiler)->compilerStr << "\"" << endl;
227         detectedCompiler = CC_UNKNOWN;
228     }
229     return detectedCompiler;
230 #endif
231 }
232
233 /*!
234     Returns true if the \a executable could be loaded, else false.
235     This means that the executable either is in the current directory
236     or in the PATH.
237 */
238 bool Environment::detectExecutable(const QString &executable)
239 {
240     PROCESS_INFORMATION procInfo;
241     memset(&procInfo, 0, sizeof(procInfo));
242
243     STARTUPINFO startInfo;
244     memset(&startInfo, 0, sizeof(startInfo));
245     startInfo.cb = sizeof(startInfo);
246
247     bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),
248                                       0, 0, false,
249                                       CREATE_NO_WINDOW | CREATE_SUSPENDED,
250                                       0, 0, &startInfo, &procInfo);
251
252     if (couldExecute) {
253         CloseHandle(procInfo.hThread);
254         TerminateProcess(procInfo.hProcess, 0);
255         CloseHandle(procInfo.hProcess);
256     }
257     return couldExecute;
258 }
259
260 /*!
261   Determine the g++ version.
262 */
263
264 int Environment::detectGPlusPlusVersion(const QString &executable)
265 {
266     QRegExp regexp(QLatin1String("[gG]\\+\\+[\\.exEX]{0,4} ([^\\n]+) (\\d+)\\.(\\d+)\\.(\\d+)"));
267     QString stdOut = readProcessStandardOutput(executable + QLatin1String(" --version"));
268     if (regexp.indexIn(stdOut) != -1) {
269         const QString compiler = regexp.cap(1);
270         const int major = regexp.cap(2).toInt();
271         const int minor = regexp.cap(3).toInt();
272         const int patch = regexp.cap(4).toInt();
273         return (major << 16) + (minor << 8) + patch;
274     }
275     return 0;
276 }
277
278 /*!
279     Run a process and return its standard output.
280 */
281
282 QString Environment::readProcessStandardOutput(const QString &commandLine)
283 {
284     QString stdOut;
285     TCHAR tempFileName[MAX_PATH];
286     TCHAR tempPathBuffer[MAX_PATH];
287     if (!GetTempPath(MAX_PATH, tempPathBuffer)
288         || !GetTempFileName(tempPathBuffer, TEXT("qtconfigure"), 0, tempFileName))
289         return stdOut;
290
291     STARTUPINFO startInfo;
292     memset(&startInfo, 0, sizeof(startInfo));
293     startInfo.cb = sizeof(startInfo);
294     startInfo.dwFlags |= STARTF_USESTDHANDLES;
295
296     SECURITY_ATTRIBUTES securityAttributes;
297     securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
298     securityAttributes.bInheritHandle = TRUE;
299     securityAttributes.lpSecurityDescriptor = NULL;
300
301     startInfo.hStdOutput = CreateFile(tempFileName, GENERIC_WRITE, 0, &securityAttributes, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
302     if (startInfo.hStdOutput == INVALID_HANDLE_VALUE)
303         return stdOut;
304
305     PROCESS_INFORMATION procInfo;
306     memset(&procInfo, 0, sizeof(procInfo));
307
308     if (!CreateProcess(0, (wchar_t*)commandLine.utf16(),
309                        0, 0, TRUE,
310                        0,
311                        0, 0, &startInfo, &procInfo)) {
312         CloseHandle(startInfo.hStdOutput);
313         DeleteFile(tempFileName);
314         return stdOut;
315     }
316
317     WaitForSingleObject(procInfo.hProcess, INFINITE);
318     CloseHandle(procInfo.hThread);
319     CloseHandle(procInfo.hProcess);
320     CloseHandle(startInfo.hStdOutput);
321     QFile file(QString::fromWCharArray(tempFileName));
322
323     if (file.open(QIODevice::Text| QIODevice::ReadOnly)) {
324         stdOut = QString::fromLocal8Bit(file.readAll());
325         file.close();
326     }
327     DeleteFile(tempFileName);
328     return stdOut;
329 }
330
331 /*!
332     Creates a commandling from \a program and it \a arguments,
333     escaping characters that needs it.
334 */
335 static QString qt_create_commandline(const QString &program, const QStringList &arguments)
336 {
337     QString programName = program;
338     if (!programName.startsWith("\"") && !programName.endsWith("\"") && programName.contains(" "))
339         programName = "\"" + programName + "\"";
340     programName.replace("/", "\\");
341
342     QString args;
343     // add the prgram as the first arrg ... it works better
344     args = programName + " ";
345     for (int i=0; i<arguments.size(); ++i) {
346         QString tmp = arguments.at(i);
347         // in the case of \" already being in the string the \ must also be escaped
348         tmp.replace( "\\\"", "\\\\\"" );
349         // escape a single " because the arguments will be parsed
350         tmp.replace( "\"", "\\\"" );
351         if (tmp.isEmpty() || tmp.contains(' ') || tmp.contains('\t')) {
352             // The argument must not end with a \ since this would be interpreted
353             // as escaping the quote -- rather put the \ behind the quote: e.g.
354             // rather use "foo"\ than "foo\"
355             QString endQuote("\"");
356             int i = tmp.length();
357             while (i>0 && tmp.at(i-1) == '\\') {
358                 --i;
359                 endQuote += "\\";
360             }
361             args += QString(" \"") + tmp.left(i) + endQuote;
362         } else {
363             args += ' ' + tmp;
364         }
365     }
366     return args;
367 }
368
369 /*!
370     Creates a QByteArray of the \a environment.
371 */
372 static QByteArray qt_create_environment(const QStringList &environment)
373 {
374     QByteArray envlist;
375     if (environment.isEmpty())
376         return envlist;
377
378     int pos = 0;
379     // add PATH if necessary (for DLL loading)
380     QByteArray path = qgetenv("PATH");
381     if (environment.filter(QRegExp("^PATH=",Qt::CaseInsensitive)).isEmpty() && !path.isNull()) {
382             QString tmp = QString(QLatin1String("PATH=%1")).arg(QString::fromLocal8Bit(path));
383             uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
384             envlist.resize(envlist.size() + tmpSize);
385             memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
386             pos += tmpSize;
387     }
388     // add the user environment
389     foreach (const QString &tmp, environment) {
390             uint tmpSize = sizeof(wchar_t) * (tmp.length() + 1);
391             envlist.resize(envlist.size() + tmpSize);
392             memcpy(envlist.data() + pos, tmp.utf16(), tmpSize);
393             pos += tmpSize;
394     }
395     // add the 2 terminating 0 (actually 4, just to be on the safe side)
396     envlist.resize(envlist.size() + 4);
397     envlist[pos++] = 0;
398     envlist[pos++] = 0;
399     envlist[pos++] = 0;
400     envlist[pos++] = 0;
401
402     return envlist;
403 }
404
405 /*!
406     Executes the command described in \a arguments, in the
407     environment inherited from the parent process, with the
408     \a additionalEnv settings applied.
409     \a removeEnv removes the specified environment variables from
410     the environment of the executed process.
411
412     Returns the exit value of the process, or -1 if the command could
413     not be executed.
414
415     This function uses _(w)spawnvpe to spawn a process by searching
416     through the PATH environment variable.
417 */
418 int Environment::execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv)
419 {
420 #ifdef CONFIGURE_DEBUG_EXECUTE
421     qDebug() << "About to Execute: " << arguments;
422     qDebug() << "   " << QDir::currentPath();
423     qDebug() << "   " << additionalEnv;
424     qDebug() << "   " << removeEnv;
425 #endif
426     // Create the full environment from the current environment and
427     // the additionalEnv strings, then remove all variables defined
428     // in removeEnv
429     QMap<QString, QString> fullEnvMap;
430     LPWSTR envStrings = GetEnvironmentStrings();
431     if (envStrings) {
432         int strLen = 0;
433         for (LPWSTR envString = envStrings; *(envString); envString += strLen + 1) {
434             strLen = wcslen(envString);
435             QString str = QString((const QChar*)envString, strLen);
436             if (!str.startsWith("=")) { // These are added by the system
437                 int sepIndex = str.indexOf('=');
438                 fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
439             }
440         }
441     }
442     FreeEnvironmentStrings(envStrings);
443
444     // Add additionalEnv variables
445     for (int i = 0; i < additionalEnv.count(); ++i) {
446         const QString &str = additionalEnv.at(i);
447         int sepIndex = str.indexOf('=');
448         fullEnvMap.insert(str.left(sepIndex).toUpper(), str.mid(sepIndex +1));
449     }
450
451     // Remove removeEnv variables
452     for (int j = 0; j < removeEnv.count(); ++j)
453         fullEnvMap.remove(removeEnv.at(j).toUpper());
454
455     // Add all variables to a QStringList
456     QStringList fullEnv;
457     QMapIterator<QString, QString> it(fullEnvMap);
458     while (it.hasNext()) {
459         it.next();
460         fullEnv += QString(it.key() + "=" + it.value());
461     }
462
463     // ----------------------------
464     QString program = arguments.takeAt(0);
465     QString args = qt_create_commandline(program, arguments);
466     QByteArray envlist = qt_create_environment(fullEnv);
467
468     DWORD exitCode = DWORD(-1);
469     PROCESS_INFORMATION procInfo;
470     memset(&procInfo, 0, sizeof(procInfo));
471
472     STARTUPINFO startInfo;
473     memset(&startInfo, 0, sizeof(startInfo));
474     startInfo.cb = sizeof(startInfo);
475
476     bool couldExecute = CreateProcess(0, (wchar_t*)args.utf16(),
477                                       0, 0, true, CREATE_UNICODE_ENVIRONMENT,
478                                       envlist.isEmpty() ? 0 : envlist.data(),
479                                       0, &startInfo, &procInfo);
480
481     if (couldExecute) {
482         WaitForSingleObject(procInfo.hProcess, INFINITE);
483         GetExitCodeProcess(procInfo.hProcess, &exitCode);
484         CloseHandle(procInfo.hThread);
485         CloseHandle(procInfo.hProcess);
486     }
487
488
489     if (exitCode == DWORD(-1)) {
490         switch(GetLastError()) {
491         case E2BIG:
492             cerr << "execute: Argument list exceeds 1024 bytes" << endl;
493             foreach (const QString &arg, arguments)
494                 cerr << "   (" << arg.toLocal8Bit().constData() << ")" << endl;
495             break;
496         case ENOENT:
497             cerr << "execute: File or path is not found (" << program.toLocal8Bit().constData() << ")" << endl;
498             break;
499         case ENOEXEC:
500             cerr << "execute: Specified file is not executable or has invalid executable-file format (" << program.toLocal8Bit().constData() << ")" << endl;
501             break;
502         case ENOMEM:
503             cerr << "execute: Not enough memory is available to execute new process." << endl;
504             break;
505         default:
506             cerr << "execute: Unknown error" << endl;
507             foreach (const QString &arg, arguments)
508                 cerr << "   (" << arg.toLocal8Bit().constData() << ")" << endl;
509             break;
510         }
511     }
512     return exitCode;
513 }
514
515 /*!
516     Copies the \a srcDir contents into \a destDir.
517
518     If \a includeSrcDir is not empty, any files with 'h', 'prf', or 'conf' suffixes
519     will not be copied over from \a srcDir. Instead a new file will be created
520     in \a destDir with the same name and that file will include a file with the
521     same name from the \a includeSrcDir using relative path and appropriate
522     syntax for the file type.
523
524     Returns true if copying was successful.
525 */
526 bool Environment::cpdir(const QString &srcDir,
527                         const QString &destDir,
528                         const QString &includeSrcDir)
529 {
530     QString cleanSrcName = QDir::cleanPath(srcDir);
531     QString cleanDstName = QDir::cleanPath(destDir);
532     QString cleanIncludeName = QDir::cleanPath(includeSrcDir);
533
534 #ifdef CONFIGURE_DEBUG_CP_DIR
535     qDebug() << "Attempt to cpdir " << cleanSrcName << "->" << cleanDstName;
536 #endif
537     if(!QFile::exists(cleanDstName) && !QDir().mkpath(cleanDstName)) {
538         qDebug() << "cpdir: Failure to create " << cleanDstName;
539         return false;
540     }
541
542     bool result = true;
543     QDir dir = QDir(cleanSrcName);
544     QDir destinationDir = QDir(cleanDstName);
545     QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
546     for (int i = 0; result && (i < allEntries.count()); ++i) {
547         QFileInfo entry = allEntries.at(i);
548         bool intermediate = true;
549         if (entry.isDir()) {
550             QString newIncSrcDir;
551             if (!includeSrcDir.isEmpty())
552                 newIncSrcDir = QString("%1/%2").arg(cleanIncludeName).arg(entry.fileName());
553
554             intermediate = cpdir(QString("%1/%2").arg(cleanSrcName).arg(entry.fileName()),
555                                  QString("%1/%2").arg(cleanDstName).arg(entry.fileName()),
556                                  newIncSrcDir);
557         } else {
558             QString destFile = QString("%1/%2").arg(cleanDstName).arg(entry.fileName());
559 #ifdef CONFIGURE_DEBUG_CP_DIR
560             qDebug() << "About to cp (file)" << entry.absoluteFilePath() << "->" << destFile;
561 #endif
562             QFile::remove(destFile);
563             QString suffix = entry.suffix();
564             if (!includeSrcDir.isEmpty() && (suffix == "prf" || suffix == "conf" || suffix == "h")) {
565                 QString relativeIncludeFilePath = QString("%1/%2").arg(cleanIncludeName).arg(entry.fileName());
566                 relativeIncludeFilePath = destinationDir.relativeFilePath(relativeIncludeFilePath);
567 #ifdef CONFIGURE_DEBUG_CP_DIR
568                 qDebug() << "...instead generate relative include to" << relativeIncludeFilePath;
569 #endif
570                 QFile currentFile(destFile);
571                 if (currentFile.open(QFile::WriteOnly | QFile::Text)) {
572                     QTextStream fileStream;
573                     fileStream.setDevice(&currentFile);
574
575                     if (suffix == "prf" || suffix == "conf") {
576                         if (entry.fileName() == "qmake.conf") {
577                             // While QMAKESPEC_ORIGINAL being relative or absolute doesn't matter for the
578                             // primary use of this variable by qmake to identify the original mkspec, the
579                             // variable is also used for few special cases where the absolute path is required.
580                             // Conversely, the include of the original qmake.conf must be done using relative path,
581                             // as some Qt binary deployments are done in a manner that doesn't allow for patching
582                             // the paths at the installation time.
583                             fileStream << "QMAKESPEC_ORIGINAL=" << cleanSrcName << endl << endl;
584                         }
585                         fileStream << "include(" << relativeIncludeFilePath << ")" << endl << endl;
586                     } else if (suffix == "h") {
587                         fileStream << "#include \"" << relativeIncludeFilePath << "\"" << endl << endl;
588                     }
589
590                     fileStream.flush();
591                     currentFile.close();
592                 }
593             } else {
594                 intermediate = QFile::copy(entry.absoluteFilePath(), destFile);
595                 SetFileAttributes((wchar_t*)destFile.utf16(), FILE_ATTRIBUTE_NORMAL);
596             }
597         }
598         if(!intermediate) {
599             qDebug() << "cpdir: Failure for " << entry.fileName() << entry.isDir();
600             result = false;
601         }
602     }
603     return result;
604 }
605
606 bool Environment::rmdir(const QString &name)
607 {
608     bool result = true;
609     QString cleanName = QDir::cleanPath(name);
610
611     QDir dir = QDir(cleanName);
612     QFileInfoList allEntries = dir.entryInfoList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
613     for (int i = 0; result && (i < allEntries.count()); ++i) {
614         QFileInfo entry = allEntries.at(i);
615         if (entry.isDir()) {
616             result &= rmdir(entry.absoluteFilePath());
617         } else {
618             result &= QFile::remove(entry.absoluteFilePath());
619         }
620     }
621     result &= dir.rmdir(cleanName);
622     return result;
623 }
624
625 QString Environment::symbianEpocRoot()
626 {
627     // Call function defined in tools/shared/symbian/epocroot_p.h
628     return ::qt_epocRoot();
629 }
630
631 QT_END_NAMESPACE