Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "utils.h"
30#include "elfreader.h"
31
32#include <QtCore/QString>
33#include <QtCore/QDebug>
34#include <QtCore/QDir>
35#include <QtCore/QFile>
36#include <QtCore/QFileInfo>
37#include <QtCore/QTemporaryFile>
38#include <QtCore/QScopedPointer>
39#include <QtCore/QScopedArrayPointer>
40#include <QtCore/QStandardPaths>
41#if defined(Q_OS_WIN)
42# include <QtCore/qt_windows.h>
43# include <shlwapi.h>
44# include <delayimp.h>
45#else // Q_OS_WIN
46# include <sys/wait.h>
47# include <sys/types.h>
48# include <sys/stat.h>
49# include <unistd.h>
50# include <stdlib.h>
51# include <string.h>
52# include <errno.h>
53# include <fcntl.h>
54#endif // !Q_OS_WIN
55
56QT_BEGIN_NAMESPACE
57
58int optVerboseLevel = 1;
59
60bool isBuildDirectory(Platform platform, const QString &dirName)
61{
62 return (platform & WindowsBased) && (dirName == QLatin1String("debug") || dirName == QLatin1String("release"));
63}
64
65// Create a symbolic link by changing to the source directory to make sure the
66// link uses relative paths only (QFile::link() otherwise uses the absolute path).
67bool createSymbolicLink(const QFileInfo &source, const QString &target, QString *errorMessage)
68{
69 const QString oldDirectory = QDir::currentPath();
70 if (!QDir::setCurrent(source.absolutePath())) {
71 *errorMessage = QStringLiteral("Unable to change to directory %1.").arg(QDir::toNativeSeparators(source.absolutePath()));
72 return false;
73 }
74 QFile file(source.fileName());
75 const bool success = file.link(target);
76 QDir::setCurrent(oldDirectory);
77 if (!success) {
78 *errorMessage = QString::fromLatin1("Failed to create symbolic link %1 -> %2: %3")
79 .arg(QDir::toNativeSeparators(source.absoluteFilePath()),
80 QDir::toNativeSeparators(target), file.errorString());
81 return false;
82 }
83 return true;
84}
85
86bool createDirectory(const QString &directory, QString *errorMessage)
87{
88 const QFileInfo fi(directory);
89 if (fi.isDir())
90 return true;
91 if (fi.exists()) {
92 *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.").
93 arg(QDir::toNativeSeparators(directory));
94 return false;
95 }
96 if (optVerboseLevel)
97 std::wcout << "Creating " << QDir::toNativeSeparators(directory) << "...\n";
98 QDir dir;
99 if (!dir.mkpath(directory)) {
100 *errorMessage = QString::fromLatin1("Could not create directory %1.").
101 arg(QDir::toNativeSeparators(directory));
102 return false;
103 }
104 return true;
105}
106
107// Find shared libraries matching debug/Platform in a directory, return relative names.
108QStringList findSharedLibraries(const QDir &directory, Platform platform,
109 DebugMatchMode debugMatchMode,
110 const QString &prefix)
111{
112 QString nameFilter = prefix;
113 if (nameFilter.isEmpty())
114 nameFilter += QLatin1Char('*');
115 if (debugMatchMode == MatchDebug && (platform & WindowsBased))
116 nameFilter += QLatin1Char('d');
117 nameFilter += sharedLibrarySuffix(platform);
118 QStringList result;
119 QString errorMessage;
120 const QFileInfoList &dlls = directory.entryInfoList(QStringList(nameFilter), QDir::Files);
121 for (const QFileInfo &dllFi : dlls) {
122 const QString dllPath = dllFi.absoluteFilePath();
123 bool matches = true;
124 if (debugMatchMode != MatchDebugOrRelease && (platform & WindowsBased)) {
125 bool debugDll;
126 if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll,
127 (platform == WindowsDesktopMinGW))) {
128 matches = debugDll == (debugMatchMode == MatchDebug);
129 } else {
130 std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(dllPath)
131 << ": " << errorMessage;
132 }
133 } // Windows
134 if (matches)
135 result += dllFi.fileName();
136 } // for
137 return result;
138}
139
140#ifdef Q_OS_WIN
141QString winErrorMessage(unsigned long error)
142{
143 QString rc = QString::fromLatin1("#%1: ").arg(error);
144 ushort *lpMsgBuf;
145
146 const DWORD len = FormatMessage(
147 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
148 NULL, error, 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL);
149 if (len) {
150 rc = QString::fromUtf16(lpMsgBuf, int(len));
151 LocalFree(lpMsgBuf);
152 } else {
153 rc += QString::fromLatin1("<unknown error>");
154 }
155 return rc;
156}
157
158// Case-Normalize file name via GetShortPathNameW()/GetLongPathNameW()
159QString normalizeFileName(const QString &name)
160{
161 wchar_t shortBuffer[MAX_PATH];
162 const QString nativeFileName = QDir::toNativeSeparators(name);
163 if (!GetShortPathNameW(reinterpret_cast<LPCWSTR>(nativeFileName.utf16()), shortBuffer, MAX_PATH))
164 return name;
165 wchar_t result[MAX_PATH];
166 if (!GetLongPathNameW(shortBuffer, result, MAX_PATH))
167 return name;
168 return QDir::fromNativeSeparators(QString::fromWCharArray(result));
169}
170
171// Find a tool binary in the Windows SDK 8
172QString findSdkTool(const QString &tool)
173{
174 QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(QLatin1Char(';'));
175 const QByteArray sdkDir = qgetenv("WindowsSdkDir");
176 if (!sdkDir.isEmpty())
177 paths.prepend(QDir::cleanPath(QString::fromLocal8Bit(sdkDir)) + QLatin1String("/Tools/x64"));
178 return QStandardPaths::findExecutable(tool, paths);
179}
180
181// runProcess helper: Create a temporary file for stdout/stderr redirection.
182static HANDLE createInheritableTemporaryFile()
183{
184 wchar_t path[MAX_PATH];
185 if (!GetTempPath(MAX_PATH, path))
186 return INVALID_HANDLE_VALUE;
187 wchar_t name[MAX_PATH];
188 if (!GetTempFileName(path, L"temp", 0, name)) // Creates file.
189 return INVALID_HANDLE_VALUE;
190 SECURITY_ATTRIBUTES securityAttributes;
191 ZeroMemory(&securityAttributes, sizeof(securityAttributes));
192 securityAttributes.nLength = sizeof(securityAttributes);
193 securityAttributes.bInheritHandle = TRUE;
194 return CreateFile(name, GENERIC_READ | GENERIC_WRITE,
195 FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
196 TRUNCATE_EXISTING,
197 FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
198}
199
200// runProcess helper: Rewind and read out a temporary file for stdout/stderr.
201static inline void readTemporaryProcessFile(HANDLE handle, QByteArray *result)
202{
203 if (SetFilePointer(handle, 0, 0, FILE_BEGIN) == 0xFFFFFFFF)
204 return;
205 char buf[1024];
206 DWORD bytesRead;
207 while (ReadFile(handle, buf, sizeof(buf), &bytesRead, NULL) && bytesRead)
208 result->append(buf, int(bytesRead));
209 CloseHandle(handle);
210}
211
212static inline void appendToCommandLine(const QString &argument, QString *commandLine)
213{
214 const bool needsQuote = argument.contains(QLatin1Char(' '));
215 if (!commandLine->isEmpty())
216 commandLine->append(QLatin1Char(' '));
217 if (needsQuote)
218 commandLine->append(QLatin1Char('"'));
219 commandLine->append(argument);
220 if (needsQuote)
221 commandLine->append(QLatin1Char('"'));
222}
223
224// runProcess: Run a command line process (replacement for QProcess which
225// does not exist in the bootstrap library).
226bool runProcess(const QString &binary, const QStringList &args,
227 const QString &workingDirectory,
228 unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
229 QString *errorMessage)
230{
231 if (exitCode)
232 *exitCode = 0;
233
234 STARTUPINFO si;
235 ZeroMemory(&si, sizeof(si));
236 si.cb = sizeof(si);
237
238 STARTUPINFO myInfo;
239 GetStartupInfo(&myInfo);
240 si.hStdInput = myInfo.hStdInput;
241 si.hStdOutput = myInfo.hStdOutput;
242 si.hStdError = myInfo.hStdError;
243
244 PROCESS_INFORMATION pi;
245 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
246 const QChar backSlash = QLatin1Char('\\');
247 QString nativeWorkingDir = QDir::toNativeSeparators(workingDirectory.isEmpty() ? QDir::currentPath() : workingDirectory);
248 if (!nativeWorkingDir.endsWith(backSlash))
249 nativeWorkingDir += backSlash;
250
251 if (stdOut) {
252 si.hStdOutput = createInheritableTemporaryFile();
253 if (si.hStdOutput == INVALID_HANDLE_VALUE) {
254 if (errorMessage)
255 *errorMessage = QStringLiteral("Error creating stdout temporary file");
256 return false;
257 }
258 si.dwFlags |= STARTF_USESTDHANDLES;
259 }
260
261 if (stdErr) {
262 si.hStdError = createInheritableTemporaryFile();
263 if (si.hStdError == INVALID_HANDLE_VALUE) {
264 if (errorMessage)
265 *errorMessage = QStringLiteral("Error creating stderr temporary file");
266 return false;
267 }
268 si.dwFlags |= STARTF_USESTDHANDLES;
269 }
270
271 // Create a copy of the command line which CreateProcessW can modify.
272 QString commandLine;
273 appendToCommandLine(binary, &commandLine);
274 for (const QString &a : args)
275 appendToCommandLine(a, &commandLine);
276 if (optVerboseLevel > 1)
277 std::wcout << "Running: " << commandLine << '\n';
278
279 QScopedArrayPointer<wchar_t> commandLineW(new wchar_t[commandLine.size() + 1]);
280 commandLine.toWCharArray(commandLineW.data());
281 commandLineW[commandLine.size()] = 0;
282 if (!CreateProcessW(0, commandLineW.data(), 0, 0, /* InheritHandles */ TRUE, 0, 0,
283 reinterpret_cast<LPCWSTR>(nativeWorkingDir.utf16()), &si, &pi)) {
284 if (stdOut)
285 CloseHandle(si.hStdOutput);
286 if (stdErr)
287 CloseHandle(si.hStdError);
288 if (errorMessage)
289 *errorMessage = QStringLiteral("CreateProcessW failed: ") + winErrorMessage(GetLastError());
290 return false;
291 }
292
293 WaitForSingleObject(pi.hProcess, INFINITE);
294 CloseHandle(pi.hThread);
295 if (exitCode)
296 GetExitCodeProcess(pi.hProcess, exitCode);
297 CloseHandle(pi.hProcess);
298
299 if (stdOut)
300 readTemporaryProcessFile(si.hStdOutput, stdOut);
301 if (stdErr)
302 readTemporaryProcessFile(si.hStdError, stdErr);
303 return true;
304}
305
306bool runElevatedBackgroundProcess(const QString &binary, const QStringList &args, Qt::HANDLE *processHandle)
307{
308 QScopedArrayPointer<wchar_t> binaryW(new wchar_t[binary.size() + 1]);
309 binary.toWCharArray(binaryW.data());
310 binaryW[binary.size()] = 0;
311
312 const QString arguments = args.join(QLatin1Char(' '));
313 QScopedArrayPointer<wchar_t> argumentsW(new wchar_t[arguments.size() + 1]);
314 arguments.toWCharArray(argumentsW.data());
315 argumentsW[arguments.size()] = 0;
316
317 SHELLEXECUTEINFO shellExecute = {};
318 shellExecute.cbSize = sizeof(shellExecute);
319 shellExecute.fMask = SEE_MASK_NOCLOSEPROCESS;
320 shellExecute.hwnd = 0;
321 shellExecute.lpVerb = L"runas"; // causes elevation
322 shellExecute.lpFile = binaryW.data();
323 shellExecute.lpParameters = argumentsW.data();
324 shellExecute.lpDirectory = 0;
325 shellExecute.nShow = SW_SHOW;
326 shellExecute.hInstApp = 0;
327
328 bool ret = ShellExecuteEx(&shellExecute);
329
330 if (processHandle)
331 *processHandle = shellExecute.hProcess;
332
333 return ret;
334}
335
336#else // Q_OS_WIN
337
338static inline char *encodeFileName(const QString &f)
339{
340 const QByteArray encoded = QFile::encodeName(f);
341 char *result = new char[encoded.size() + 1];
342 strcpy(result, encoded.constData());
343 return result;
344}
345
346static inline char *tempFilePattern()
347{
348 QString path = QDir::tempPath();
349 if (!path.endsWith(QLatin1Char('/')))
350 path += QLatin1Char('/');
351 path += QStringLiteral("tmpXXXXXX");
352 return encodeFileName(path);
353}
354
355static inline QByteArray readOutRedirectFile(int fd)
356{
357 enum { bufSize = 256 };
358
359 QByteArray result;
360 if (!lseek(fd, 0, 0)) {
361 char buf[bufSize];
362 while (true) {
363 const ssize_t rs = read(fd, buf, bufSize);
364 if (rs <= 0)
365 break;
366 result.append(buf, int(rs));
367 }
368 }
369 close(fd);
370 return result;
371}
372
373// runProcess: Run a command line process (replacement for QProcess which
374// does not exist in the bootstrap library).
375bool runProcess(const QString &binary, const QStringList &args,
376 const QString &workingDirectory,
377 unsigned long *exitCode, QByteArray *stdOut, QByteArray *stdErr,
378 QString *errorMessage)
379{
380 QScopedArrayPointer<char> stdOutFileName;
381 QScopedArrayPointer<char> stdErrFileName;
382
383 int stdOutFile = 0;
384 if (stdOut) {
385 stdOutFileName.reset(tempFilePattern());
386 stdOutFile = mkstemp(stdOutFileName.data());
387 if (stdOutFile < 0) {
388 *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
389 return false;
390 }
391 }
392
393 int stdErrFile = 0;
394 if (stdErr) {
395 stdErrFileName.reset(tempFilePattern());
396 stdErrFile = mkstemp(stdErrFileName.data());
397 if (stdErrFile < 0) {
398 *errorMessage = QStringLiteral("mkstemp() failed: ") + QString::fromLocal8Bit(strerror(errno));
399 return false;
400 }
401 }
402
403 const pid_t pID = fork();
404
405 if (pID < 0) {
406 *errorMessage = QStringLiteral("Fork failed: ") + QString::fromLocal8Bit(strerror(errno));
407 return false;
408 }
409
410 if (!pID) { // Child
411 if (stdOut) {
412 dup2(stdOutFile, STDOUT_FILENO);
413 close(stdOutFile);
414 }
415 if (stdErr) {
416 dup2(stdErrFile, STDERR_FILENO);
417 close(stdErrFile);
418 }
419
420 if (!workingDirectory.isEmpty() && !QDir::setCurrent(workingDirectory)) {
421 std::wcerr << "Failed to change working directory to " << workingDirectory << ".\n";
422 ::_exit(-1);
423 }
424
425 char **argv = new char *[args.size() + 2]; // Create argv.
426 char **ap = argv;
427 *ap++ = encodeFileName(binary);
428 for (const QString &a : qAsConst(args))
429 *ap++ = encodeFileName(a);
430 *ap = 0;
431
432 execvp(argv[0], argv);
433 ::_exit(-1);
434 }
435
436 int status;
437 pid_t waitResult;
438
439 do {
440 waitResult = waitpid(pID, &status, 0);
441 } while (waitResult == -1 && errno == EINTR);
442
443 if (stdOut) {
444 *stdOut = readOutRedirectFile(stdOutFile);
445 unlink(stdOutFileName.data());
446 }
447 if (stdErr) {
448 *stdErr = readOutRedirectFile(stdErrFile);
449 unlink(stdErrFileName.data());
450 }
451
452 if (waitResult < 0) {
453 *errorMessage = QStringLiteral("Wait failed: ") + QString::fromLocal8Bit(strerror(errno));
454 return false;
455 }
456 if (!WIFEXITED(status)) {
457 *errorMessage = binary + QStringLiteral(" did not exit cleanly.");
458 return false;
459 }
460 if (exitCode)
461 *exitCode = WEXITSTATUS(status);
462 return true;
463}
464
465bool runElevatedBackgroundProcess(const QString &binary, const QStringList &args, Qt::HANDLE *processHandle)
466{
467 Q_UNUSED(binary);
468 Q_UNUSED(args);
469 Q_UNUSED(processHandle);
470 Q_UNIMPLEMENTED();
471 return false;
472}
473
474#endif // !Q_OS_WIN
475
476// Find a file in the path using ShellAPI. This can be used to locate DLLs which
477// QStandardPaths cannot do.
478QString findInPath(const QString &file)
479{
480#if defined(Q_OS_WIN)
481 if (file.size() < MAX_PATH - 1) {
482 wchar_t buffer[MAX_PATH];
483 file.toWCharArray(buffer);
484 buffer[file.size()] = 0;
485 if (PathFindOnPath(buffer, NULL))
486 return QDir::cleanPath(QString::fromWCharArray(buffer));
487 }
488 return QString();
489#else // Q_OS_WIN
490 return QStandardPaths::findExecutable(file);
491#endif // !Q_OS_WIN
492}
493
494const char *qmakeInfixKey = "QT_INFIX";
495
496QMap<QString, QString> queryQMakeAll(QString *errorMessage)
497{
498 QByteArray stdOut;
499 QByteArray stdErr;
500 unsigned long exitCode = 0;
501 const QString binary = QStringLiteral("qmake");
502 if (!runProcess(binary, QStringList(QStringLiteral("-query")), QString(), &exitCode, &stdOut, &stdErr, errorMessage))
503 return QMap<QString, QString>();
504 if (exitCode) {
505 *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
506 + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
507 return QMap<QString, QString>();
508 }
509 const QString output = QString::fromLocal8Bit(stdOut).trimmed().remove(QLatin1Char('\r'));
510 QMap<QString, QString> result;
511 const int size = output.size();
512 for (int pos = 0; pos < size; ) {
513 const int colonPos = output.indexOf(QLatin1Char(':'), pos);
514 if (colonPos < 0)
515 break;
516 int endPos = output.indexOf(QLatin1Char('\n'), colonPos + 1);
517 if (endPos < 0)
518 endPos = size;
519 const QString key = output.mid(pos, colonPos - pos);
520 const QString value = output.mid(colonPos + 1, endPos - colonPos - 1);
521 result.insert(key, value);
522 pos = endPos + 1;
523 }
524 QFile qconfigPriFile(result.value(QStringLiteral("QT_HOST_DATA")) + QStringLiteral("/mkspecs/qconfig.pri"));
525 if (qconfigPriFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
526 while (true) {
527 const QByteArray line = qconfigPriFile.readLine();
528 if (line.isEmpty())
529 break;
530 if (line.startsWith("QT_LIBINFIX")) {
531 const int pos = line.indexOf('=');
532 if (pos >= 0) {
533 const QString infix = QString::fromUtf8(line.right(line.size() - pos - 1).trimmed());
534 if (!infix.isEmpty())
535 result.insert(QLatin1String(qmakeInfixKey), infix);
536 }
537 break;
538 }
539 }
540 } else {
541 std::wcerr << "Warning: Unable to read " << QDir::toNativeSeparators(qconfigPriFile.fileName())
542 << ": " << qconfigPriFile.errorString()<< '\n';
543 }
544 return result;
545}
546
547QString queryQMake(const QString &variable, QString *errorMessage)
548{
549 QByteArray stdOut;
550 QByteArray stdErr;
551 unsigned long exitCode;
552 const QString binary = QStringLiteral("qmake");
553 QStringList args;
554 args << QStringLiteral("-query ") << variable;
555 if (!runProcess(binary, args, QString(), &exitCode, &stdOut, &stdErr, errorMessage))
556 return QString();
557 if (exitCode) {
558 *errorMessage = binary + QStringLiteral(" returns ") + QString::number(exitCode)
559 + QStringLiteral(": ") + QString::fromLocal8Bit(stdErr);
560 return QString();
561 }
562 return QString::fromLocal8Bit(stdOut).trimmed();
563}
564
565// Update a file or directory.
566bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
567 const QString &targetDirectory, unsigned flags, JsonOutput *json, QString *errorMessage)
568{
569 const QFileInfo sourceFileInfo(sourceFileName);
570 const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName();
571 if (optVerboseLevel > 1)
572 std::wcout << "Checking " << sourceFileName << ", " << targetFileName<< '\n';
573
574 if (!sourceFileInfo.exists()) {
575 *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
576 return false;
577 }
578
579 if (sourceFileInfo.isSymLink()) {
580 *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).")
581 .arg(QDir::toNativeSeparators(sourceFileName));
582 return false;
583 }
584
585 const QFileInfo targetFileInfo(targetFileName);
586
587 if (sourceFileInfo.isDir()) {
588 if (targetFileInfo.exists()) {
589 if (!targetFileInfo.isDir()) {
590 *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
591 .arg(QDir::toNativeSeparators(targetFileName));
592 return false;
593 } // Not a directory.
594 } else { // exists.
595 QDir d(targetDirectory);
596 if (optVerboseLevel)
597 std::wcout << "Creating " << QDir::toNativeSeparators(targetFileName) << ".\n";
598 if (!(flags & SkipUpdateFile) && !d.mkdir(sourceFileInfo.fileName())) {
599 *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
600 .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
601 return false;
602 }
603 }
604 // Recurse into directory
605 QDir dir(sourceFileName);
606 const QFileInfoList allEntries = dir.entryInfoList(nameFilters, QDir::Files)
607 + dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
608 for (const QFileInfo &entryFi : allEntries) {
609 if (!updateFile(entryFi.absoluteFilePath(), nameFilters, targetFileName, flags, json, errorMessage))
610 return false;
611 }
612 return true;
613 } // Source is directory.
614
615 if (targetFileInfo.exists()) {
616 if (!(flags & ForceUpdateFile)
617 && targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
618 if (optVerboseLevel)
619 std::wcout << sourceFileInfo.fileName() << " is up to date.\n";
620 if (json)
621 json->addFile(sourceFileName, targetDirectory);
622 return true;
623 }
624 QFile targetFile(targetFileName);
625 if (!(flags & SkipUpdateFile) && !targetFile.remove()) {
626 *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
627 .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
628 return false;
629 }
630 } // target exists
631 QFile file(sourceFileName);
632 if (optVerboseLevel)
633 std::wcout << "Updating " << sourceFileInfo.fileName() << ".\n";
634 if (!(flags & SkipUpdateFile) && !file.copy(targetFileName)) {
635 *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
636 .arg(QDir::toNativeSeparators(sourceFileName),
637 QDir::toNativeSeparators(targetFileName),
638 file.errorString());
639 return false;
640 }
641 if (json)
642 json->addFile(sourceFileName, targetDirectory);
643 return true;
644}
645
646bool readElfExecutable(const QString &elfExecutableFileName, QString *errorMessage,
647 QStringList *dependentLibraries, unsigned *wordSize,
648 bool *isDebug)
649{
650 ElfReader elfReader(elfExecutableFileName);
651 const ElfData data = elfReader.readHeaders();
652 if (data.sectionHeaders.isEmpty()) {
653 *errorMessage = QStringLiteral("Unable to read ELF binary \"")
654 + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ")
655 + elfReader.errorString();
656 return false;
657 }
658 if (wordSize)
659 *wordSize = data.elfclass == Elf_ELFCLASS64 ? 64 : 32;
660 if (dependentLibraries) {
661 dependentLibraries->clear();
662 const QList<QByteArray> libs = elfReader.dependencies();
663 if (libs.isEmpty()) {
664 *errorMessage = QStringLiteral("Unable to read dependenices of ELF binary \"")
665 + QDir::toNativeSeparators(elfExecutableFileName) + QStringLiteral("\": ")
666 + elfReader.errorString();
667 return false;
668 }
669 for (const QByteArray &l : libs)
670 dependentLibraries->push_back(QString::fromLocal8Bit(l));
671 }
672 if (isDebug)
673 *isDebug = data.symbolsType != UnknownSymbols && data.symbolsType != NoSymbols;
674 return true;
675}
676
677#ifdef Q_OS_WIN
678
679static inline QString stringFromRvaPtr(const void *rvaPtr)
680{
681 return QString::fromLocal8Bit(static_cast<const char *>(rvaPtr));
682}
683
684// Helper for reading out PE executable files: Find a section header for an RVA
685// (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
686template <class ImageNtHeader>
687const IMAGE_SECTION_HEADER *findSectionHeader(DWORD rva, const ImageNtHeader *nTHeader)
688{
689 const IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nTHeader);
690 const IMAGE_SECTION_HEADER *sectionEnd = section + nTHeader->FileHeader.NumberOfSections;
691 for ( ; section < sectionEnd; ++section)
692 if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize))
693 return section;
694 return 0;
695}
696
697// Helper for reading out PE executable files: convert RVA to pointer (IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32).
698template <class ImageNtHeader>
699inline const void *rvaToPtr(DWORD rva, const ImageNtHeader *nTHeader, const void *imageBase)
700{
701 const IMAGE_SECTION_HEADER *sectionHdr = findSectionHeader(rva, nTHeader);
702 if (!sectionHdr)
703 return 0;
704 const DWORD delta = sectionHdr->VirtualAddress - sectionHdr->PointerToRawData;
705 return static_cast<const char *>(imageBase) + rva - delta;
706}
707
708// Helper for reading out PE executable files: return word size of a IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32
709template <class ImageNtHeader>
710inline unsigned ntHeaderWordSize(const ImageNtHeader *header)
711{
712 // defines IMAGE_NT_OPTIONAL_HDR32_MAGIC, IMAGE_NT_OPTIONAL_HDR64_MAGIC
713 enum { imageNtOptionlHeader32Magic = 0x10b, imageNtOptionlHeader64Magic = 0x20b };
714 if (header->OptionalHeader.Magic == imageNtOptionlHeader32Magic)
715 return 32;
716 if (header->OptionalHeader.Magic == imageNtOptionlHeader64Magic)
717 return 64;
718 return 0;
719}
720
721// Helper for reading out PE executable files: Retrieve the NT image header of an
722// executable via the legacy DOS header.
723static IMAGE_NT_HEADERS *getNtHeader(void *fileMemory, QString *errorMessage)
724{
725 IMAGE_DOS_HEADER *dosHeader = static_cast<PIMAGE_DOS_HEADER>(fileMemory);
726 // Check DOS header consistency
727 if (IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER))
728 || dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
729 *errorMessage = QString::fromLatin1("DOS header check failed.");
730 return 0;
731 }
732 // Retrieve NT header
733 char *ntHeaderC = static_cast<char *>(fileMemory) + dosHeader->e_lfanew;
734 IMAGE_NT_HEADERS *ntHeaders = reinterpret_cast<IMAGE_NT_HEADERS *>(ntHeaderC);
735 // check NT header consistency
736 if (IsBadReadPtr(ntHeaders, sizeof(ntHeaders->Signature))
737 || ntHeaders->Signature != IMAGE_NT_SIGNATURE
738 || IsBadReadPtr(&ntHeaders->FileHeader, sizeof(IMAGE_FILE_HEADER))) {
739 *errorMessage = QString::fromLatin1("NT header check failed.");
740 return 0;
741 }
742 // Check magic
743 if (!ntHeaderWordSize(ntHeaders)) {
744 *errorMessage = QString::fromLatin1("NT header check failed; magic %1 is invalid.").
745 arg(ntHeaders->OptionalHeader.Magic);
746 return 0;
747 }
748 // Check section headers
749 IMAGE_SECTION_HEADER *sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
750 if (IsBadReadPtr(sectionHeaders, ntHeaders->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))) {
751 *errorMessage = QString::fromLatin1("NT header section header check failed.");
752 return 0;
753 }
754 return ntHeaders;
755}
756
757// Helper for reading out PE executable files: Read out import sections from
758// IMAGE_NT_HEADERS64, IMAGE_NT_HEADERS32.
759template <class ImageNtHeader>
760inline QStringList readImportSections(const ImageNtHeader *ntHeaders, const void *base, QString *errorMessage)
761{
762 // Get import directory entry RVA and read out
763 const DWORD importsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
764 if (!importsStartRVA) {
765 *errorMessage = QString::fromLatin1("Failed to find IMAGE_DIRECTORY_ENTRY_IMPORT entry.");
766 return QStringList();
767 }
768 const IMAGE_IMPORT_DESCRIPTOR *importDesc = static_cast<const IMAGE_IMPORT_DESCRIPTOR *>(rvaToPtr(importsStartRVA, ntHeaders, base));
769 if (!importDesc) {
770 *errorMessage = QString::fromLatin1("Failed to find IMAGE_IMPORT_DESCRIPTOR entry.");
771 return QStringList();
772 }
773 QStringList result;
774 for ( ; importDesc->Name; ++importDesc)
775 result.push_back(stringFromRvaPtr(rvaToPtr(importDesc->Name, ntHeaders, base)));
776
777 // Read delay-loaded DLLs, see http://msdn.microsoft.com/en-us/magazine/cc301808.aspx .
778 // Check on grAttr bit 1 whether this is the format using RVA's > VS 6
779 if (const DWORD delayedImportsStartRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress) {
780 const ImgDelayDescr *delayedImportDesc = static_cast<const ImgDelayDescr *>(rvaToPtr(delayedImportsStartRVA, ntHeaders, base));
781 for ( ; delayedImportDesc->rvaDLLName && (delayedImportDesc->grAttrs & 1); ++delayedImportDesc)
782 result.push_back(stringFromRvaPtr(rvaToPtr(delayedImportDesc->rvaDLLName, ntHeaders, base)));
783 }
784
785 return result;
786}
787
788// Check for MSCV runtime (MSVCP90D.dll/MSVCP90.dll, MSVCP120D.dll/MSVCP120.dll,
789// VCRUNTIME140D.DLL/VCRUNTIME140.DLL (VS2015) or msvcp120d_app.dll/msvcp120_app.dll).
790enum MsvcDebugRuntimeResult { MsvcDebugRuntime, MsvcReleaseRuntime, NoMsvcRuntime };
791
792static inline MsvcDebugRuntimeResult checkMsvcDebugRuntime(const QStringList &dependentLibraries)
793{
794 for (const QString &lib : dependentLibraries) {
795 int pos = 0;
796 if (lib.startsWith(QLatin1String("MSVCR"), Qt::CaseInsensitive)
797 || lib.startsWith(QLatin1String("MSVCP"), Qt::CaseInsensitive)) {
798 pos = 5;
799 } else if (lib.startsWith(QLatin1String("VCRUNTIME"), Qt::CaseInsensitive)) {
800 pos = 9;
801 }
802 if (pos && lib.at(pos).isDigit()) {
803 for (++pos; lib.at(pos).isDigit(); ++pos)
804 ;
805 return lib.at(pos).toLower() == QLatin1Char('d')
806 ? MsvcDebugRuntime : MsvcReleaseRuntime;
807 }
808 }
809 return NoMsvcRuntime;
810}
811
812template <class ImageNtHeader>
813inline void determineDebugAndDependentLibs(const ImageNtHeader *nth, const void *fileMemory,
814 bool isMinGW,
815 QStringList *dependentLibrariesIn,
816 bool *isDebugIn, QString *errorMessage)
817{
818 const bool hasDebugEntry = nth->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
819 QStringList dependentLibraries;
820 if (dependentLibrariesIn || (isDebugIn != nullptr && hasDebugEntry && !isMinGW))
821 dependentLibraries = readImportSections(nth, fileMemory, errorMessage);
822
823 if (dependentLibrariesIn)
824 *dependentLibrariesIn = dependentLibraries;
825 if (isDebugIn != nullptr) {
826 if (isMinGW) {
827 // Use logic that's used e.g. in objdump / pfd library
828 *isDebugIn = !(nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED);
829 } else {
830 // When an MSVC debug entry is present, check whether the debug runtime
831 // is actually used to detect -release / -force-debug-info builds.
832 *isDebugIn = hasDebugEntry && checkMsvcDebugRuntime(dependentLibraries) != MsvcReleaseRuntime;
833 }
834 }
835}
836
837// Read a PE executable and determine dependent libraries, word size
838// and debug flags.
839bool readPeExecutable(const QString &peExecutableFileName, QString *errorMessage,
840 QStringList *dependentLibrariesIn, unsigned *wordSizeIn,
841 bool *isDebugIn, bool isMinGW, unsigned short *machineArchIn)
842{
843 bool result = false;
844 HANDLE hFile = NULL;
845 HANDLE hFileMap = NULL;
846 void *fileMemory = 0;
847
848 if (dependentLibrariesIn)
849 dependentLibrariesIn->clear();
850 if (wordSizeIn)
851 *wordSizeIn = 0;
852 if (isDebugIn)
853 *isDebugIn = false;
854
855 do {
856 // Create a memory mapping of the file
857 hFile = CreateFile(reinterpret_cast<const WCHAR*>(peExecutableFileName.utf16()), GENERIC_READ, FILE_SHARE_READ, NULL,
858 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
859 if (hFile == INVALID_HANDLE_VALUE || hFile == NULL) {
860 *errorMessage = QString::fromLatin1("Cannot open '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
861 break;
862 }
863
864 hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
865 if (hFileMap == NULL) {
866 *errorMessage = QString::fromLatin1("Cannot create file mapping of '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
867 break;
868 }
869
870 fileMemory = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
871 if (!fileMemory) {
872 *errorMessage = QString::fromLatin1("Cannot map '%1': %2").arg(peExecutableFileName, winErrorMessage(GetLastError()));
873 break;
874 }
875
876 const IMAGE_NT_HEADERS *ntHeaders = getNtHeader(fileMemory, errorMessage);
877 if (!ntHeaders)
878 break;
879
880 const unsigned wordSize = ntHeaderWordSize(ntHeaders);
881 if (wordSizeIn)
882 *wordSizeIn = wordSize;
883 if (wordSize == 32) {
884 determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS32 *>(ntHeaders),
885 fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
886 } else {
887 determineDebugAndDependentLibs(reinterpret_cast<const IMAGE_NT_HEADERS64 *>(ntHeaders),
888 fileMemory, isMinGW, dependentLibrariesIn, isDebugIn, errorMessage);
889 }
890
891 if (machineArchIn)
892 *machineArchIn = ntHeaders->FileHeader.Machine;
893
894 result = true;
895 if (optVerboseLevel > 1) {
896 std::wcout << __FUNCTION__ << ": " << QDir::toNativeSeparators(peExecutableFileName)
897 << ' ' << wordSize << " bit";
898 if (isMinGW)
899 std::wcout << ", MinGW";
900 if (dependentLibrariesIn) {
901 std::wcout << ", dependent libraries: ";
902 if (optVerboseLevel > 2)
903 std::wcout << dependentLibrariesIn->join(QLatin1Char(' '));
904 else
905 std::wcout << dependentLibrariesIn->size();
906 }
907 if (isDebugIn)
908 std::wcout << (*isDebugIn ? ", debug" : ", release");
909 std::wcout << '\n';
910 }
911 } while (false);
912
913 if (fileMemory)
914 UnmapViewOfFile(fileMemory);
915
916 if (hFileMap != NULL)
917 CloseHandle(hFileMap);
918
919 if (hFile != NULL && hFile != INVALID_HANDLE_VALUE)
920 CloseHandle(hFile);
921
922 return result;
923}
924
925QString findD3dCompiler(Platform platform, const QString &qtBinDir, unsigned wordSize)
926{
927 const QString prefix = QStringLiteral("D3Dcompiler_");
928 const QString suffix = QLatin1String(windowsSharedLibrarySuffix);
929 // Get the DLL from Kit 8.0 onwards
930 const QString kitDir = QString::fromLocal8Bit(qgetenv("WindowsSdkDir"));
931 if (!kitDir.isEmpty()) {
932 QString redistDirPath = QDir::cleanPath(kitDir) + QStringLiteral("/Redist/D3D/");
933 if (platform & ArmBased) {
934 redistDirPath += QStringLiteral("arm");
935 } else {
936 redistDirPath += wordSize == 32 ? QStringLiteral("x86") : QStringLiteral("x64");
937 }
938 QDir redistDir(redistDirPath);
939 if (redistDir.exists()) {
940 const QFileInfoList files = redistDir.entryInfoList(QStringList(prefix + QLatin1Char('*') + suffix), QDir::Files);
941 if (!files.isEmpty())
942 return files.front().absoluteFilePath();
943 }
944 }
945 QStringList candidateVersions;
946 for (int i = 47 ; i >= 40 ; --i)
947 candidateVersions.append(prefix + QString::number(i) + suffix);
948 // Check the bin directory of the Qt SDK (in case it is shadowed by the
949 // Windows system directory in PATH).
950 for (const QString &candidate : qAsConst(candidateVersions)) {
951 const QFileInfo fi(qtBinDir + QLatin1Char('/') + candidate);
952 if (fi.isFile())
953 return fi.absoluteFilePath();
954 }
955 // Find the latest D3D compiler DLL in path (Windows 8.1 has d3dcompiler_47).
956 if (platform & IntelBased) {
957 QString errorMessage;
958 unsigned detectedWordSize;
959 for (const QString &candidate : qAsConst(candidateVersions)) {
960 const QString dll = findInPath(candidate);
961 if (!dll.isEmpty()
962 && readPeExecutable(dll, &errorMessage, 0, &detectedWordSize, 0)
963 && detectedWordSize == wordSize) {
964 return dll;
965 }
966 }
967 }
968 return QString();
969}
970
971#else // Q_OS_WIN
972
973bool readPeExecutable(const QString &, QString *errorMessage,
974 QStringList *, unsigned *, bool *, bool, unsigned short *)
975{
976 *errorMessage = QStringLiteral("Not implemented.");
977 return false;
978}
979
980QString findD3dCompiler(Platform, const QString &, unsigned)
981{
982 return QString();
983}
984
985#endif // !Q_OS_WIN
986
987// Search for "qt_prfxpath=xxxx" in \a path, and replace it with "qt_prfxpath=."
988bool patchQtCore(const QString &path, QString *errorMessage)
989{
990 if (optVerboseLevel)
991 std::wcout << "Patching " << QFileInfo(path).fileName() << "...\n";
992
993 QFile file(path);
994 if (!file.open(QIODevice::ReadWrite)) {
995 *errorMessage = QString::fromLatin1("Unable to patch %1: %2").arg(
996 QDir::toNativeSeparators(path), file.errorString());
997 return false;
998 }
999 QByteArray content = file.readAll();
1000
1001 if (content.isEmpty()) {
1002 *errorMessage = QString::fromLatin1("Unable to patch %1: Could not read file content").arg(
1003 QDir::toNativeSeparators(path));
1004 return false;
1005 }
1006
1007 QByteArray prfxpath("qt_prfxpath=");
1008 int startPos = content.indexOf(prfxpath);
1009 if (startPos == -1) {
1010 *errorMessage = QString::fromLatin1(
1011 "Unable to patch %1: Could not locate pattern \"qt_prfxpath=\"").arg(
1012 QDir::toNativeSeparators(path));
1013 return false;
1014 }
1015 startPos += prfxpath.length();
1016 int endPos = content.indexOf(char(0), startPos);
1017 if (endPos == -1) {
1018 *errorMessage = QString::fromLatin1("Unable to patch %1: Internal error").arg(
1019 QDir::toNativeSeparators(path));
1020 return false;
1021 }
1022
1023 QByteArray replacement = QByteArray(endPos - startPos, char(0));
1024 replacement[0] = '.';
1025 content.replace(startPos, endPos - startPos, replacement);
1026
1027 if (!file.seek(0)
1028 || (file.write(content) != content.size())) {
1029 *errorMessage = QString::fromLatin1("Unable to patch %1: Could not write to file").arg(
1030 QDir::toNativeSeparators(path));
1031 return false;
1032 }
1033 return true;
1034}
1035
1036#ifdef Q_OS_WIN
1037QString getArchString(unsigned short machineArch)
1038{
1039 switch (machineArch) {
1040 case IMAGE_FILE_MACHINE_I386:
1041 return QStringLiteral("x86");
1042 case IMAGE_FILE_MACHINE_ARM:
1043 return QStringLiteral("arm");
1044 case IMAGE_FILE_MACHINE_AMD64:
1045 return QStringLiteral("x64");
1046 case IMAGE_FILE_MACHINE_ARM64:
1047 return QStringLiteral("arm64");
1048 default:
1049 break;
1050 }
1051 return QString();
1052}
1053#endif // Q_OS_WIN
1054
1055QT_END_NAMESPACE
1056

Warning: That file was not part of the compilation database. It may have many parsing errors.