Remove incorrect check for mingw 64 bit
[qt:qt.git] / tools / configure / environment.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 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.
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 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.
24 **
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.
28 **
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.
36 **
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_UNKNOWN, "Unknown", 0, 0},
88 };
89
90
91 // Initialize static variables
92 Compiler Environment::detectedCompiler = CC_UNKNOWN;
93
94 /*!
95     Returns the pointer to the CompilerInfo for a \a compiler.
96 */
97 CompilerInfo *Environment::compilerInfo(Compiler compiler)
98 {
99     int i = 0;
100     while(compiler_info[i].compiler != compiler && compiler_info[i].compiler != CC_UNKNOWN)
101         ++i;
102     return &(compiler_info[i]);
103 }
104
105 /*!
106     Returns the qmakespec for the compiler detected on the system.
107 */
108 QString Environment::detectQMakeSpec()
109 {
110     QString spec;
111     switch (detectCompiler()) {
112     case CC_NET2013:
113         spec = "win32-msvc2013";
114         break;
115     case CC_NET2012:
116         spec = "win32-msvc2012";
117         break;
118     case CC_NET2010:
119         spec = "win32-msvc2010";
120         break;
121     case CC_NET2008:
122         spec = "win32-msvc2008";
123         break;
124     case CC_NET2005:
125         spec = "win32-msvc2005";
126         break;
127     case CC_NET2003:
128         spec = "win32-msvc2003";
129         break;
130     case CC_INTEL:
131         spec = "win32-icc";
132         break;
133     case CC_MINGW:
134         spec = "win32-g++-4.6";
135         break;
136     case CC_MINGW_44:
137         spec = "win32-g++";
138         break;
139     case CC_BORLAND:
140         spec = "win32-borland";
141         break;
142     default:
143         break;
144     }
145
146     return spec;
147 }
148
149 /*!
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
152     compiler_info list.
153
154     If more than one compiler is found, CC_UNKNOWN is returned.
155 */
156 Compiler Environment::detectCompiler()
157 {
158 #ifndef Q_OS_WIN32
159     return CC_UNKNOWN; // Always generate CC_UNKNOWN on other platforms
160 #else
161     if(detectedCompiler != CC_UNKNOWN)
162         return detectedCompiler;
163
164     int installed = 0;
165
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) {
176                         ++installed;
177                         detectedCompiler = compiler_info[i].compiler;
178                     }
179                     /* else {
180
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.
184
185                     }
186                     */
187                     break;
188                 }
189             }
190         }
191     }
192
193     // Now just go looking for the executables, and accept any executable as the lowest version
194     if (!installed) {
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) {
199                     ++installed;
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;
205                     }
206                 }
207                 /* else {
208
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.
212
213                 }
214                 */
215                 break;
216             }
217         }
218     }
219
220     if (installed > 1) {
221         cout << "Found more than one known compiler! Using \"" << compilerInfo(detectedCompiler)->compilerStr << "\"" << endl;
222         detectedCompiler = CC_UNKNOWN;
223     }
224     return detectedCompiler;
225 #endif
226 }
227
228 /*!
229     Returns true if the \a executable could be loaded, else false.
230     This means that the executable either is in the current directory
231     or in the PATH.
232 */
233 bool Environment::detectExecutable(const QString &executable)
234 {
235     PROCESS_INFORMATION procInfo;
236     memset(&procInfo, 0, sizeof(procInfo));
237
238     STARTUPINFO startInfo;
239     memset(&startInfo, 0, sizeof(startInfo));
240     startInfo.cb = sizeof(startInfo);
241
242     bool couldExecute = CreateProcess(0, (wchar_t*)executable.utf16(),
243                                       0, 0, false,
244                                       CREATE_NO_WINDOW | CREATE_SUSPENDED,
245                                       0, 0, &startInfo, &procInfo);
246
247     if (couldExecute) {
248         CloseHandle(procInfo.hThread);
249         TerminateProcess(procInfo.hProcess, 0);
250         CloseHandle(procInfo.hProcess);
251     }
252     return couldExecute;
253 }
254
255 /*!
256   Determine the g++ version.
257 */
258
259 int Environment::detectGPlusPlusVersion(const QString &executable)
260 {
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;
269     }
270     return 0;
271 }
272
273 /*!
274     Run a process and return its standard output.
275 */
276
277 QString Environment::readProcessStandardOutput(const QString &commandLine)
278 {
279     QString stdOut;
280     TCHAR tempFileName[MAX_PATH];
281     TCHAR tempPathBuffer[MAX_PATH];
282     if (!GetTempPath(MAX_PATH, tempPathBuffer)
283         || !GetTempFileName(tempPathBuffer, TEXT("qtconfigure"), 0, tempFileName))
284         return stdOut;
285
286     STARTUPINFO startInfo;
287     memset(&startInfo, 0, sizeof(startInfo));
288     startInfo.cb = sizeof(startInfo);
289     startInfo.dwFlags |= STARTF_USESTDHANDLES;
290
291     SECURITY_ATTRIBUTES securityAttributes;
292     securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
293     securityAttributes.bInheritHandle = TRUE;
294     securityAttributes.lpSecurityDescriptor = NULL;
295
296     startInfo.hStdOutput = CreateFile(tempFileName, GENERIC_WRITE, 0, &securityAttributes, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
297     if (startInfo.hStdOutput == INVALID_HANDLE_VALUE)
298         return stdOut;
299
300     PROCESS_INFORMATION procInfo;
301     memset(&procInfo, 0, sizeof(procInfo));
302
303     if (!CreateProcess(0, (wchar_t*)commandLine.utf16(),
304                        0, 0, TRUE,
305                        0,
306                        0, 0, &startInfo, &procInfo)) {
307         CloseHandle(startInfo.hStdOutput);
308         DeleteFile(tempFileName);
309         return stdOut;
310     }
311
312     WaitForSingleObject(procInfo.hProcess, INFINITE);
313     CloseHandle(procInfo.hThread);
314     CloseHandle(procInfo.hProcess);
315     CloseHandle(startInfo.hStdOutput);
316     QFile file(QString::fromWCharArray(tempFileName));
317
318     if (file.open(QIODevice::Text| QIODevice::ReadOnly)) {
319         stdOut = QString::fromLocal8Bit(file.readAll());
320         file.close();
321     }
322     DeleteFile(tempFileName);
323     return stdOut;
324 }
325
326 /*!
327     Creates a commandling from \a program and it \a arguments,
328     escaping characters that needs it.
329 */
330 static QString qt_create_commandline(const QString &program, const QStringList &arguments)
331 {
332     QString programName = program;
333     if (!programName.startsWith("\"") && !programName.endsWith("\"") && programName.contains(" "))
334         programName = "\"" + programName + "\"";
335     programName.replace("/", "\\");
336
337     QString args;
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) == '\\') {
353                 --i;
354                 endQuote += "\\";
355             }
356             args += QString(" \"") + tmp.left(i) + endQuote;
357         } else {
358             args += ' ' + tmp;
359         }
360     }
361     return args;
362 }
363
364 /*!
365     Creates a QByteArray of the \a environment.
366 */
367 static QByteArray qt_create_environment(const QStringList &environment)
368 {
369     QByteArray envlist;
370     if (environment.isEmpty())
371         return envlist;
372
373     int pos = 0;
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);
381             pos += tmpSize;
382     }
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);
388             pos += tmpSize;
389     }
390     // add the 2 terminating 0 (actually 4, just to be on the safe side)
391     envlist.resize(envlist.size() + 4);
392     envlist[pos++] = 0;
393     envlist[pos++] = 0;
394     envlist[pos++] = 0;
395     envlist[pos++] = 0;
396
397     return envlist;
398 }
399
400 /*!
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.
406
407     Returns the exit value of the process, or -1 if the command could
408     not be executed.
409
410     This function uses _(w)spawnvpe to spawn a process by searching
411     through the PATH environment variable.
412 */
413 int Environment::execute(QStringList arguments, const QStringList &additionalEnv, const QStringList &removeEnv)
414 {
415 #ifdef CONFIGURE_DEBUG_EXECUTE
416     qDebug() << "About to Execute: " << arguments;
417     qDebug() << "   " << QDir::currentPath();
418     qDebug() << "   " << additionalEnv;
419     qDebug() << "   " << removeEnv;
420 #endif
421     // Create the full environment from the current environment and
422     // the additionalEnv strings, then remove all variables defined
423     // in removeEnv
424     QMap<QString, QString> fullEnvMap;
425     LPWSTR envStrings = GetEnvironmentStrings();
426     if (envStrings) {
427         int strLen = 0;
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));
434             }
435         }
436     }
437     FreeEnvironmentStrings(envStrings);
438
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));
444     }
445
446     // Remove removeEnv variables
447     for (int j = 0; j < removeEnv.count(); ++j)
448         fullEnvMap.remove(removeEnv.at(j).toUpper());
449
450     // Add all variables to a QStringList
451     QStringList fullEnv;
452     QMapIterator<QString, QString> it(fullEnvMap);
453     while (it.hasNext()) {
454         it.next();
455         fullEnv += QString(it.key() + "=" + it.value());
456     }
457
458     // ----------------------------
459     QString program = arguments.takeAt(0);
460     QString args = qt_create_commandline(program, arguments);
461     QByteArray envlist = qt_create_environment(fullEnv);
462
463     DWORD exitCode = DWORD(-1);
464     PROCESS_INFORMATION procInfo;
465     memset(&procInfo, 0, sizeof(procInfo));
466
467     STARTUPINFO startInfo;
468     memset(&startInfo, 0, sizeof(startInfo));
469     startInfo.cb = sizeof(startInfo);
470
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);
475
476     if (couldExecute) {
477         WaitForSingleObject(procInfo.hProcess, INFINITE);
478         GetExitCodeProcess(procInfo.hProcess, &exitCode);
479         CloseHandle(procInfo.hThread);
480         CloseHandle(procInfo.hProcess);
481     }
482
483
484     if (exitCode == DWORD(-1)) {
485         switch(GetLastError()) {
486         case E2BIG:
487             cerr << "execute: Argument list exceeds 1024 bytes" << endl;
488             foreach (const QString &arg, arguments)
489                 cerr << "   (" << arg.toLocal8Bit().constData() << ")" << endl;
490             break;
491         case ENOENT:
492             cerr << "execute: File or path is not found (" << program.toLocal8Bit().constData() << ")" << endl;
493             break;
494         case ENOEXEC:
495             cerr << "execute: Specified file is not executable or has invalid executable-file format (" << program.toLocal8Bit().constData() << ")" << endl;
496             break;
497         case ENOMEM:
498             cerr << "execute: Not enough memory is available to execute new process." << endl;
499             break;
500         default:
501             cerr << "execute: Unknown error" << endl;
502             foreach (const QString &arg, arguments)
503                 cerr << "   (" << arg.toLocal8Bit().constData() << ")" << endl;
504             break;
505         }
506     }
507     return exitCode;
508 }
509
510 /*!
511     Copies the \a srcDir contents into \a destDir.
512
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.
518
519     Returns true if copying was successful.
520 */
521 bool Environment::cpdir(const QString &srcDir,
522                         const QString &destDir,
523                         const QString &includeSrcDir)
524 {
525     QString cleanSrcName = QDir::cleanPath(srcDir);
526     QString cleanDstName = QDir::cleanPath(destDir);
527     QString cleanIncludeName = QDir::cleanPath(includeSrcDir);
528
529 #ifdef CONFIGURE_DEBUG_CP_DIR
530     qDebug() << "Attempt to cpdir " << cleanSrcName << "->" << cleanDstName;
531 #endif
532     if(!QFile::exists(cleanDstName) && !QDir().mkpath(cleanDstName)) {
533         qDebug() << "cpdir: Failure to create " << cleanDstName;
534         return false;
535     }
536
537     bool result = true;
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;
544         if (entry.isDir()) {
545             QString newIncSrcDir;
546             if (!includeSrcDir.isEmpty())
547                 newIncSrcDir = QString("%1/%2").arg(cleanIncludeName).arg(entry.fileName());
548
549             intermediate = cpdir(QString("%1/%2").arg(cleanSrcName).arg(entry.fileName()),
550                                  QString("%1/%2").arg(cleanDstName).arg(entry.fileName()),
551                                  newIncSrcDir);
552         } else {
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;
556 #endif
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;
564 #endif
565                 QFile currentFile(destFile);
566                 if (currentFile.open(QFile::WriteOnly | QFile::Text)) {
567                     QTextStream fileStream;
568                     fileStream.setDevice(&currentFile);
569
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;
579                         }
580                         fileStream << "include(" << relativeIncludeFilePath << ")" << endl << endl;
581                     } else if (suffix == "h") {
582                         fileStream << "#include \"" << relativeIncludeFilePath << "\"" << endl << endl;
583                     }
584
585                     fileStream.flush();
586                     currentFile.close();
587                 }
588             } else {
589                 intermediate = QFile::copy(entry.absoluteFilePath(), destFile);
590                 SetFileAttributes((wchar_t*)destFile.utf16(), FILE_ATTRIBUTE_NORMAL);
591             }
592         }
593         if(!intermediate) {
594             qDebug() << "cpdir: Failure for " << entry.fileName() << entry.isDir();
595             result = false;
596         }
597     }
598     return result;
599 }
600
601 bool Environment::rmdir(const QString &name)
602 {
603     bool result = true;
604     QString cleanName = QDir::cleanPath(name);
605
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);
610         if (entry.isDir()) {
611             result &= rmdir(entry.absoluteFilePath());
612         } else {
613             result &= QFile::remove(entry.absoluteFilePath());
614         }
615     }
616     result &= dir.rmdir(cleanName);
617     return result;
618 }
619
620 QString Environment::symbianEpocRoot()
621 {
622     // Call function defined in tools/shared/symbian/epocroot_p.h
623     return ::qt_epocRoot();
624 }
625
626 QT_END_NAMESPACE