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
{
116
117
    if (!Core::ICore::instance())
        return QString();
dt's avatar
dt committed
118
119
    const QString dumperSourcePath = Core::ICore::instance()->resourcePath() + QLatin1String("/gdbmacros/");
    QDateTime lastModified = QFileInfo(dumperSourcePath + "gdbmacros.cpp").lastModified();
120
121
122
123
    // 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
124

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

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
165
166
// 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)
167
168
169
170
171
{
    // Locations to try:
    //    $QTDIR/qtc-debugging-helper
    //    $APPLICATION-DIR/qtc-debugging-helper/$hash
    //    $USERDIR/qtc-debugging-helper/$hash
172
    const QStringList directories = DebuggingHelperLibrary::debuggingHelperLibraryDirectories(qtInstallData);
173
174

    QStringList files;
175
    files << QLatin1String("gdbmacros.cpp") << QLatin1String("gdbmacros_p.h") << QLatin1String("gdbmacros.h") << QLatin1String("gdbmacros.pro")
176
177
178
179
180
          << 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();
181
            return directory;
182
183
184
185
        }
    *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();
186
187
}

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

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

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

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

    output += proc.readAll();

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

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

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