autotoolsproject.cpp 10.8 KB
Newer Older
1
/****************************************************************************
2
**
3 4 5 6
** Copyright (C) 2016 Openismus GmbH.
** Author: Peter Penz (ppenz@openismus.com)
** Author: Patricia Santana Cruz (patriciasantanacruz@gmail.com)
** Contact: https://www.qt.io/licensing/
7
**
hjk's avatar
hjk committed
8
** This file is part of Qt Creator.
9
**
hjk's avatar
hjk committed
10 11 12 13
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
14 15 16
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
17
**
18 19 20 21 22 23 24
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25
**
hjk's avatar
hjk committed
26
****************************************************************************/
27 28

#include "autotoolsproject.h"
Tobias Hunger's avatar
Tobias Hunger committed
29
#include "autotoolsbuildconfiguration.h"
30 31 32 33 34 35 36
#include "autotoolsprojectconstants.h"
#include "autotoolsprojectnode.h"
#include "autotoolsopenprojectwizard.h"
#include "makestep.h"
#include "makefileparserthread.h"

#include <projectexplorer/abi.h>
37
#include <projectexplorer/toolchain.h>
Tobias Hunger's avatar
Tobias Hunger committed
38 39
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/kitinformation.h>
40 41 42
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h>
#include <projectexplorer/projectexplorerconstants.h>
Tobias Hunger's avatar
Tobias Hunger committed
43
#include <projectexplorer/target.h>
44
#include <projectexplorer/headerpath.h>
45
#include <extensionsystem/pluginmanager.h>
46
#include <cpptools/cppmodelmanager.h>
47
#include <cpptools/projectinfo.h>
48
#include <cpptools/cppprojectupdater.h>
49
#include <coreplugin/icore.h>
50
#include <coreplugin/icontext.h>
51 52
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
53
#include <utils/algorithm.h>
54
#include <utils/qtcassert.h>
55
#include <utils/filesystemwatcher.h>
56

57 58 59 60 61 62 63 64
#include <QFileInfo>
#include <QTimer>
#include <QPointer>
#include <QApplication>
#include <QCursor>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
65 66 67 68 69

using namespace AutotoolsProjectManager;
using namespace AutotoolsProjectManager::Internal;
using namespace ProjectExplorer;

70
AutotoolsProject::AutotoolsProject(const Utils::FileName &fileName) :
71
    Project(Constants::MAKEFILE_MIMETYPE, fileName),
72 73
    m_fileWatcher(new Utils::FileSystemWatcher(this)),
    m_cppCodeModelUpdater(new CppTools::CppProjectUpdater(this))
74
{
75
    setId(Constants::AUTOTOOLS_PROJECT_ID);
76
    setProjectContext(Core::Context(Constants::PROJECT_CONTEXT));
77
    setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
78
    setDisplayName(projectDirectory().fileName());
79 80 81 82
}

AutotoolsProject::~AutotoolsProject()
{
83 84
    delete m_cppCodeModelUpdater;

85
    setRootProjectNode(nullptr);
86

87
    if (m_makefileParserThread) {
88 89
        m_makefileParserThread->wait();
        delete m_makefileParserThread;
90
        m_makefileParserThread = nullptr;
91 92 93
    }
}

94 95 96
QString AutotoolsProject::defaultBuildDirectory(const QString &projectPath)
{
    return QFileInfo(projectPath).absolutePath();
97 98 99 100
}

// This function, is called at the very beginning, to
// restore the settings if there are some stored.
101
Project::RestoreResult AutotoolsProject::fromMap(const QVariantMap &map, QString *errorMessage)
102
{
103 104 105
    RestoreResult result = Project::fromMap(map, errorMessage);
    if (result != RestoreResult::Ok)
        return result;
106

Montel Laurent's avatar
Montel Laurent committed
107 108
    connect(m_fileWatcher, &Utils::FileSystemWatcher::fileChanged,
            this, &AutotoolsProject::onFileChanged);
109 110

    // Load the project tree structure.
111
    loadProjectTree();
Tobias Hunger's avatar
Tobias Hunger committed
112

113
    Kit *defaultKit = KitManager::defaultKit();
Tobias Hunger's avatar
Tobias Hunger committed
114 115
    if (!activeTarget() && defaultKit)
        addTarget(createTarget(defaultKit));
116

117
    return RestoreResult::Ok;
118 119
}

120
void AutotoolsProject::loadProjectTree()
121
{
122
    emitParsingStarted();
123
    if (m_makefileParserThread) {
124 125 126
        // The thread is still busy parsing a previus configuration.
        // Wait until the thread has been finished and delete it.
        // TODO: Discuss whether blocking is acceptable.
127 128
        disconnect(m_makefileParserThread, &QThread::finished,
                   this, &AutotoolsProject::makefileParsingFinished);
129 130 131 132 133 134
        m_makefileParserThread->wait();
        delete m_makefileParserThread;
        m_makefileParserThread = 0;
    }

    // Parse the makefile asynchronously in a thread
135
    m_makefileParserThread = new MakefileParserThread(projectFilePath().toString());
136

Montel Laurent's avatar
Montel Laurent committed
137 138
    connect(m_makefileParserThread, &MakefileParserThread::started,
            this, &AutotoolsProject::makefileParsingStarted);
139

Montel Laurent's avatar
Montel Laurent committed
140 141
    connect(m_makefileParserThread, &MakefileParserThread::finished,
            this, &AutotoolsProject::makefileParsingFinished);
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    m_makefileParserThread->start();
}

void AutotoolsProject::makefileParsingStarted()
{
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
}

void AutotoolsProject::makefileParsingFinished()
{
    // The finished() signal is from a previous makefile-parser-thread
    // and can be skipped. This can happen, if the thread has emitted the
    // finished() signal during the execution of AutotoolsProject::loadProjectTree().
    // In this case the signal is in the message queue already and deleting
    // the thread of course does not remove the signal again.
    if (sender() != m_makefileParserThread)
        return;

    QApplication::restoreOverrideCursor();

    if (m_makefileParserThread->isCanceled()) {
        // The parsing has been cancelled by the user. Don't show any
        // project data at all.
        m_makefileParserThread->deleteLater();
166
        m_makefileParserThread = nullptr;
167 168 169 170 171 172 173 174 175 176 177 178 179 180
        return;
    }

    if (m_makefileParserThread->hasError())
        qWarning("Parsing of makefile contained errors.");

    // Remove file watches for the current project state.
    // The file watches will be added again after the parsing.
    foreach (const QString& watchedFile, m_watchedFiles)
        m_fileWatcher->removeFile(watchedFile);

    m_watchedFiles.clear();

    // Apply sources to m_files, which are returned at AutotoolsProject::files()
181
    const QFileInfo fileInfo = projectFilePath().toFileInfo();
182
    const QDir dir = fileInfo.absoluteDir();
183
    const QStringList files = m_makefileParserThread->sources();
184 185 186 187 188 189
    foreach (const QString& file, files)
        m_files.append(dir.absoluteFilePath(file));

    // Watch for changes of Makefile.am files. If a Makefile.am file
    // has been changed, the project tree must be reparsed.
    const QStringList makefiles = m_makefileParserThread->makefiles();
hjk's avatar
hjk committed
190
    foreach (const QString &makefile, makefiles) {
191
        const QString absMakefile = dir.absoluteFilePath(makefile);
192

193 194 195 196
        m_files.append(absMakefile);

        m_fileWatcher->addFile(absMakefile, Utils::FileSystemWatcher::WatchAllChanges);
        m_watchedFiles.append(absMakefile);
197 198
    }

hjk's avatar
hjk committed
199
    // Add configure.ac file to project and watch for changes.
200
    const QLatin1String configureAc(QLatin1String("configure.ac"));
201
    const QFile configureAcFile(fileInfo.absolutePath() + QLatin1Char('/') + configureAc);
202
    if (configureAcFile.exists()) {
203 204 205 206 207
        const QString absConfigureAc = dir.absoluteFilePath(configureAc);
        m_files.append(absConfigureAc);

        m_fileWatcher->addFile(absConfigureAc, Utils::FileSystemWatcher::WatchAllChanges);
        m_watchedFiles.append(absConfigureAc);
208 209
    }

210
    auto newRoot = new AutotoolsProjectNode(projectDirectory());
211 212 213
    for (const QString &f : m_files) {
        const Utils::FileName path = Utils::FileName::fromString(f);
        newRoot->addNestedNode(new FileNode(path, FileNode::fileTypeForFileName(path), false));
214
    }
215
    setRootProjectNode(newRoot);
216

217 218 219
    updateCppCodeModel();

    m_makefileParserThread->deleteLater();
220
    m_makefileParserThread = nullptr;
221

222
    emitParsingFinished(true);
223 224 225 226 227
}

void AutotoolsProject::onFileChanged(const QString &file)
{
    Q_UNUSED(file);
228
    loadProjectTree();
229 230 231 232 233 234 235 236 237 238
}

QStringList AutotoolsProject::buildTargets() const
{
    QStringList targets;
    targets.append(QLatin1String("all"));
    targets.append(QLatin1String("clean"));
    return targets;
}

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
static QStringList filterIncludes(const QString &absSrc, const QString &absBuild,
                                  const QStringList &in)
{
    QStringList result;
    foreach (const QString i, in) {
        QString out = i;
        out.replace(QLatin1String("$(top_srcdir)"), absSrc);
        out.replace(QLatin1String("$(abs_top_srcdir)"), absSrc);

        out.replace(QLatin1String("$(top_builddir)"), absBuild);
        out.replace(QLatin1String("$(abs_top_builddir)"), absBuild);

        result << out;
    }

    return result;
}

257 258
void AutotoolsProject::updateCppCodeModel()
{
259 260 261 262 263 264 265 266 267 268 269
    const Kit *k = nullptr;
    if (Target *target = activeTarget())
        k = target->kit();
    else
        k = KitManager::defaultKit();
    QTC_ASSERT(k, return);

    ToolChain *cToolChain
            = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::C_LANGUAGE_ID);
    ToolChain *cxxToolChain
            = ToolChainKitInformation::toolChain(k, ProjectExplorer::Constants::CXX_LANGUAGE_ID);
270

271
    m_cppCodeModelUpdater->cancel();
272

273
    CppTools::RawProjectPart rpp;
274
    rpp.setDisplayName(displayName());
275
    rpp.setProjectFileLocation(projectFilePath().toString());
276

277
    CppTools::ProjectPart::QtVersion activeQtVersion = CppTools::ProjectPart::NoQt;
278
    if (QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k)) {
279 280 281 282
        if (qtVersion->qtVersion() <= QtSupport::QtVersionNumber(4,8,6))
            activeQtVersion = CppTools::ProjectPart::Qt4_8_6AndOlder;
        else if (qtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
            activeQtVersion = CppTools::ProjectPart::Qt4Latest;
283 284 285 286
        else
            activeQtVersion = CppTools::ProjectPart::Qt5;
    }

287
    rpp.setQtVersion(activeQtVersion);
288 289 290 291
    const QStringList cflags = m_makefileParserThread->cflags();
    QStringList cxxflags = m_makefileParserThread->cxxflags();
    if (cxxflags.isEmpty())
        cxxflags = cflags;
292 293
    rpp.setFlagsForC({cToolChain, cflags});
    rpp.setFlagsForCxx({cxxToolChain, cxxflags});
Tobias Hunger's avatar
Tobias Hunger committed
294

295 296 297 298 299
    const QString absSrc = projectDirectory().toString();
    const Target *target = activeTarget();
    const QString absBuild = (target && target->activeBuildConfiguration())
            ? target->activeBuildConfiguration()->buildDirectory().toString() : QString();

300
    rpp.setIncludePaths(filterIncludes(absSrc, absBuild, m_makefileParserThread->includePaths()));
301
    rpp.setMacros(m_makefileParserThread->macros());
302
    rpp.setFiles(m_files);
303

304
    m_cppCodeModelUpdater->update({this, cToolChain, cxxToolChain, k, {rpp}});
305
}