debugginghelper.cpp 10.8 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
27
28
29
30
**
**************************************************************************/

#include "debugginghelper.h"
31

32
33
#include <coreplugin/icore.h>
#include <QtCore/QFileInfo>
34
#include <QtCore/QCoreApplication>
35
36
37
38
#include <QtCore/QHash>
#include <QtCore/QProcess>
#include <QtCore/QDir>
#include <QtCore/QDateTime>
39

40
41
42
43
44
45
46
47
48
#include <QtGui/QDesktopServices>

using namespace ProjectExplorer;

QString DebuggingHelperLibrary::findSystemQt(const Environment &env)
{
    QStringList paths = env.path();
    foreach (const QString &path, paths) {
        foreach (const QString &possibleCommand, possibleQMakeCommands()) {
49
            const QFileInfo qmake(path + QLatin1Char('/') + possibleCommand);
50
51
52
53
54
55
56
            if (qmake.exists()) {
                if (!qtVersionForQMake(qmake.absoluteFilePath()).isNull()) {
                    return qmake.absoluteFilePath();
                }
            }
        }
    }
57
    return QString();
58
59
}

60
QStringList DebuggingHelperLibrary::debuggingHelperLibraryDirectories(const QString &qtInstallData)
61
{
62
    const QChar slash = QLatin1Char('/');
63
    const uint hash = qHash(qtInstallData);
64
65
    QStringList directories;
    directories
66
67
68
            << (qtInstallData + QLatin1String("/qtc-debugging-helper/"))
            << QDir::cleanPath((QCoreApplication::applicationDirPath() + QLatin1String("/../qtc-debugging-helper/") + QString::number(hash))) + slash
            << (QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QLatin1String("/qtc-debugging-helper/") + QString::number(hash)) + slash;
69
70
71
72
73
74
    return directories;
}

QString DebuggingHelperLibrary::qtInstallDataDir(const QString &qmakePath)
{
    QProcess proc;
75
    proc.start(qmakePath, QStringList() << QLatin1String("-query") << QLatin1String("QT_INSTALL_DATA"));
76
77
    if (proc.waitForFinished())
        return QString(proc.readAll().trimmed());
78
    return QString();
79
80
81
82
}

// Debugging Helper Library

83
static inline bool getHelperFileInfoFor(const QString &directory, QFileInfo* info)
84
{
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    if (!info)
        return false;

    info->setFile(directory + QLatin1String("debug/gdbmacros.dll"));
    if (info->exists())
        return true;

    info->setFile(directory + QLatin1String("libgdbmacros.dylib"));
    if (info->exists())
        return true;

    info->setFile(directory + QLatin1String("libgdbmacros.so"));
    if (info->exists())
        return true;

    return false;
101
102
}

103
QStringList DebuggingHelperLibrary::debuggingHelperLibraryLocationsByInstallData(const QString &qtInstallData)
104
105
{
    QStringList result;
106
107
108
109
110
    QFileInfo fileInfo;
    foreach(const QString &directory, debuggingHelperLibraryDirectories(qtInstallData)) {
        if (getHelperFileInfoFor(directory, &fileInfo))
            result << fileInfo.filePath();
    }
111
112
113
    return result;
}

114
QString DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(const QString &qtInstallData)
115
{
dt's avatar
dt committed
116
117
    const QString dumperSourcePath = Core::ICore::instance()->resourcePath() + QLatin1String("/gdbmacros/");
    QDateTime lastModified = QFileInfo(dumperSourcePath + "gdbmacros.cpp").lastModified();
118
119
120
121
    // We pretend that the lastmodified of gdbmacros.cpp is 5 minutes before what the file system says
    // Because afer a installation from the package the modified dates of gdbmacros.cpp
    // and the actual library are close to each other, but not deterministic in one direction
    lastModified = lastModified.addSecs(-300);
dt's avatar
dt committed
122

123
    QFileInfo fileInfo;
124
    foreach(const QString &directory, debuggingHelperLibraryDirectories(qtInstallData)) {
125
126
127
128
        if (getHelperFileInfoFor(directory, &fileInfo)) {
            if (fileInfo.lastModified() >= lastModified)
                return fileInfo.filePath();
        }
129
130
131
132
    }
    return QString();
}

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copy helper source files to a target directory, replacing older files.
static bool copyDebuggingHelperFiles(const QStringList &files,
                                     const QString &targetDirectory,
                                     QString *errorMessage)
{
    const QString dumperSourcePath = Core::ICore::instance()->resourcePath() + QLatin1String("/gdbmacros/");
    if (!QDir().mkpath(targetDirectory)) {
        *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The target directory %1 could not be created.").arg(targetDirectory);
        return false;
    }
    foreach (const QString &file, files) {
        const QString source = dumperSourcePath + file;
        const QString dest = targetDirectory + file;
        const QFileInfo destInfo(dest);
        if (destInfo.exists()) {
            if (destInfo.lastModified() >= QFileInfo(source).lastModified())
                continue;
            if (!QFile::remove(dest)) {
                *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The existing file %1 could not be removed.").arg(destInfo.absoluteFilePath());
                return false;
            }
        }
        if (!QFile::copy(source, dest)) {
            *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The file %1 could not be copied to %2.").arg(source, dest);
            return false;
        }
    }
    return true;
}

QString DebuggingHelperLibrary::copyDebuggingHelperLibrary(const QString &qtInstallData,
                                                           QString *errorMessage)
165
166
167
168
169
{
    // Locations to try:
    //    $QTDIR/qtc-debugging-helper
    //    $APPLICATION-DIR/qtc-debugging-helper/$hash
    //    $USERDIR/qtc-debugging-helper/$hash
170
    const QStringList directories = DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData);
171
172

    QStringList files;
173
    files << QLatin1String("gdbmacros.cpp") << QLatin1String("gdbmacros_p.h") << QLatin1String("gdbmacros.h") << QLatin1String("gdbmacros.pro")
174
175
176
177
178
          << QLatin1String("LICENSE.LGPL") << QLatin1String("LGPL_EXCEPTION.TXT");
    // Try to find a writeable directory.
    foreach(const QString &directory, directories)
        if (copyDebuggingHelperFiles(files, directory, errorMessage)) {
            errorMessage->clear();
179
            return directory;
180
181
182
183
        }
    *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The debugger helpers could not be built in any of the directories:\n- %1\n\nReason: %2")
                    .arg(directories.join(QLatin1String("\n- ")), *errorMessage);
    return QString();
184
185
}

186
187
188
QString DebuggingHelperLibrary::buildDebuggingHelperLibrary(const QString &directory, const QString &makeCommand,
                                                            const QString &qmakeCommand, const QString &mkspec,
                                                            const Environment &env, const QString &targetMode)
189
190
{
    QString output;
191
    const QChar newline = QLatin1Char('\n');
192
193
194
195
196
197
    // Setup process
    QProcess proc;
    proc.setEnvironment(env.toStringList());
    proc.setWorkingDirectory(directory);
    proc.setProcessChannelMode(QProcess::MergedChannels);

198
199
    output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Building debugging helper library in %1\n").arg(directory);
    output += newline;
200

201
202
    const QString makeFullPath = env.searchInPath(makeCommand);
    if (QFileInfo(directory + QLatin1String("/Makefile")).exists()) {
203
        if (!makeFullPath.isEmpty()) {
204
205
206
            const QString cleanTarget = QLatin1String("distclean");
            output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Running %1 %2...\n").arg(makeFullPath, cleanTarget);
            proc.start(makeFullPath, QStringList(cleanTarget));
207
            proc.waitForFinished();
208
            output += QString::fromLocal8Bit(proc.readAll());
209
        } else {
210
            output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "%1 not found in PATH\n").arg(makeCommand);
211
212
            return output;
        }
213
    }
214
215
    output += newline;
    output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Running %1 ...\n").arg(qmakeCommand);
216

217
    QStringList makeArgs;
218
    makeArgs << targetMode << QLatin1String("-spec") << (mkspec.isEmpty() ? QString(QLatin1String("default")) : mkspec) << QLatin1String("gdbmacros.pro");
219
    proc.start(qmakeCommand, makeArgs);
220
221
222
223
    proc.waitForFinished();

    output += proc.readAll();

224
    output += newline;;
225
    if (!makeFullPath.isEmpty()) {
226
        output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "Running %1 ...\n").arg(makeFullPath);
227
228
229
230
        proc.start(makeFullPath, QStringList());
        proc.waitForFinished();
        output += proc.readAll();
    } else {
231
        output += QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "%1 not found in PATH\n").arg(makeCommand);
232
233
234
    }
    return output;
}
Daniel Molkentin's avatar
Daniel Molkentin committed
235

236
237
238
QString DebuggingHelperLibrary::qtVersionForQMake(const QString &qmakePath)
{
    QProcess qmake;
239
    qmake.start(qmakePath, QStringList(QLatin1String("--version")));
240
    if (!qmake.waitForFinished())
241
        return QString();
242
    QString output = qmake.readAllStandardOutput();
243
    QRegExp regexp(QLatin1String("(QMake version|QMake version:)[\\s]*([\\d.]*)"), Qt::CaseInsensitive);
244
    regexp.indexIn(output);
245
246
    if (regexp.cap(2).startsWith(QLatin1String("2."))) {
        QRegExp regexp2(QLatin1String("Using Qt version[\\s]*([\\d\\.]*)"), Qt::CaseInsensitive);
247
248
249
250
251
252
253
254
        regexp2.indexIn(output);
        return regexp2.cap(1);
    }
    return QString();
}

QStringList DebuggingHelperLibrary::possibleQMakeCommands()
{
Tobias Hunger's avatar
Tobias Hunger committed
255
    // On windows no one has renamed qmake, right?
256
#ifdef Q_OS_WIN
257
    return QStringList(QLatin1String("qmake.exe"));
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
258
#else
259
260
    // On unix some distributions renamed qmake to avoid clashes
    QStringList result;
261
    result << QLatin1String("qmake-qt4") << QLatin1String("qmake4") << QLatin1String("qmake");
262
    return result;
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
263
#endif
264
}