Autotools: Make the BuildSystem per BuildConfiguration

The per-target variant is intended for project managers without build
configurations.

Fixes: QTCREATORBUG-32064
Change-Id: I908627dbe11a68ee7ac4cca3a10f956630008f08
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-11-26 16:35:21 +01:00
parent 9a2c52d57b
commit 01cc0e7499
6 changed files with 170 additions and 205 deletions

View File

@@ -5,7 +5,6 @@ add_qtc_plugin(AutotoolsProjectManager
autogenstep.cpp autogenstep.h autogenstep.cpp autogenstep.h
autoreconfstep.cpp autoreconfstep.h autoreconfstep.cpp autoreconfstep.h
autotoolsbuildconfiguration.cpp autotoolsbuildconfiguration.h autotoolsbuildconfiguration.cpp autotoolsbuildconfiguration.h
autotoolsbuildsystem.cpp autotoolsbuildsystem.h
autotoolsprojectconstants.h autotoolsprojectconstants.h
autotoolsprojectmanagertr.h autotoolsprojectmanagertr.h
autotoolsprojectplugin.cpp autotoolsprojectplugin.cpp

View File

@@ -5,29 +5,191 @@
#include "autotoolsprojectconstants.h" #include "autotoolsprojectconstants.h"
#include "autotoolsprojectmanagertr.h" #include "autotoolsprojectmanagertr.h"
#include "makefileparser.h"
#include <projectexplorer/buildinfo.h> #include <projectexplorer/buildinfo.h>
#include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsteplist.h> #include <projectexplorer/buildsteplist.h>
#include <projectexplorer/buildsystem.h>
#include <projectexplorer/project.h> #include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorertr.h> #include <projectexplorer/projectexplorertr.h>
#include <projectexplorer/projectupdater.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <qtsupport/qtcppkitinfo.h>
#include <solutions/tasking/tasktreerunner.h>
#include <utils/async.h>
#include <utils/mimeconstants.h> #include <utils/mimeconstants.h>
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace Tasking;
using namespace Utils; using namespace Utils;
namespace AutotoolsProjectManager::Internal { namespace AutotoolsProjectManager::Internal {
// AutotoolsBuildConfiguration // AutotoolsBuildSystem
class AutotoolsBuildSystem final : public BuildSystem
{
public:
explicit AutotoolsBuildSystem(BuildConfiguration *bc);
private:
void triggerParsing() final;
QString name() const final { return QLatin1String("autotools"); }
/**
* Is invoked when the makefile parsing by m_makefileParserThread has
* been finished. Adds all sources and files into the project tree and
* takes care listen to file changes for Makefile.am and configure.ac
* files.
*/
void makefileParsingFinished(const MakefileParserOutputData &outputData);
/// Return value for AutotoolsProject::files()
QStringList m_files;
/// Responsible for parsing the makefiles asynchronously in a thread
Tasking::TaskTreeRunner m_parserRunner;
std::unique_ptr<ProjectUpdater> m_cppCodeModelUpdater;
};
AutotoolsBuildSystem::AutotoolsBuildSystem(BuildConfiguration *bc)
: BuildSystem(bc)
, m_cppCodeModelUpdater(ProjectUpdaterFactory::createCppProjectUpdater())
{
connect(project(), &Project::projectFileIsDirty, this, [this] { requestParse(); });
}
static void parseMakefileImpl(QPromise<MakefileParserOutputData> &promise, const QString &makefile)
{
const auto result = parseMakefile(makefile, QFuture<void>(promise.future()));
if (result)
promise.addResult(*result);
else
promise.future().cancel();
}
void AutotoolsBuildSystem::triggerParsing()
{
const Storage<std::optional<ParseGuard>> storage;
const auto onSetup = [this, storage](Async<MakefileParserOutputData> &async) {
*storage = guardParsingRun();
async.setConcurrentCallData(parseMakefileImpl, projectFilePath().path());
};
const auto onDone = [this, storage](const Async<MakefileParserOutputData> &async) {
(*storage)->markAsSuccess();
makefileParsingFinished(async.result());
};
const Group recipe {
storage,
AsyncTask<MakefileParserOutputData>(onSetup, onDone, CallDoneIf::Success)
};
m_parserRunner.start(recipe);
}
static QStringList filterIncludes(const QString &absSrc, const QString &absBuild,
const QStringList &in)
{
QStringList result;
for (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;
}
void AutotoolsBuildSystem::makefileParsingFinished(const MakefileParserOutputData &outputData)
{
m_files.clear();
QSet<FilePath> filesToWatch;
// Apply sources to m_files, which are returned at AutotoolsBuildSystem::files()
const QFileInfo fileInfo = projectFilePath().toFileInfo();
const QDir dir = fileInfo.absoluteDir();
const QStringList files = outputData.m_sources;
for (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 = outputData.m_makefiles;
for (const QString &makefile : makefiles) {
const QString absMakefile = dir.absoluteFilePath(makefile);
m_files.append(absMakefile);
filesToWatch.insert(FilePath::fromString(absMakefile));
}
// Add configure.ac file to project and watch for changes.
const QLatin1String configureAc(QLatin1String("configure.ac"));
const QFile configureAcFile(fileInfo.absolutePath() + QLatin1Char('/') + configureAc);
if (configureAcFile.exists()) {
const QString absConfigureAc = dir.absoluteFilePath(configureAc);
m_files.append(absConfigureAc);
filesToWatch.insert(FilePath::fromString(absConfigureAc));
}
auto newRoot = std::make_unique<ProjectNode>(project()->projectDirectory());
for (const QString &f : std::as_const(m_files)) {
const FilePath path = FilePath::fromString(f);
newRoot->addNestedNode(std::make_unique<FileNode>(path,
FileNode::fileTypeForFileName(path)));
}
setRootProjectNode(std::move(newRoot));
project()->setExtraProjectFiles(filesToWatch);
QtSupport::CppKitInfo kitInfo(kit());
QTC_ASSERT(kitInfo.isValid(), return );
RawProjectPart rpp;
rpp.setDisplayName(project()->displayName());
rpp.setProjectFileLocation(projectFilePath());
rpp.setQtVersion(kitInfo.projectPartQtVersion);
const QStringList cflags = outputData.m_cflags;
QStringList cxxflags = outputData.m_cxxflags;
if (cxxflags.isEmpty())
cxxflags = cflags;
const FilePath includeFileBaseDir = projectDirectory();
rpp.setFlagsForC({kitInfo.cToolchain, cflags, includeFileBaseDir});
rpp.setFlagsForCxx({kitInfo.cxxToolchain, cxxflags, includeFileBaseDir});
const QString absSrc = project()->projectDirectory().path();
BuildConfiguration *bc = target()->activeBuildConfiguration();
const QString absBuild = bc ? bc->buildDirectory().path() : QString();
rpp.setIncludePaths(filterIncludes(absSrc, absBuild, outputData.m_includePaths));
rpp.setMacros(outputData.m_macros);
rpp.setFiles(m_files);
m_cppCodeModelUpdater->update({project(), kitInfo, activeParseEnvironment(), {rpp}});
emitBuildSystemUpdated();
}
// AutotoolsBuildConfiguration
class AutotoolsBuildConfiguration final : public BuildConfiguration class AutotoolsBuildConfiguration final : public BuildConfiguration
{ {
public: public:
AutotoolsBuildConfiguration(Target *target, Id id) AutotoolsBuildConfiguration(Target *target, Id id)
: BuildConfiguration(target, id) : BuildConfiguration(target, id)
, m_buildSystem(new AutotoolsBuildSystem(this))
{ {
// /<foobar> is used so the un-changed check in setBuildDirectory() works correctly. // /<foobar> is used so the un-changed check in setBuildDirectory() works correctly.
// The leading / is to avoid the relative the path expansion in BuildConfiguration::buildDirectory. // The leading / is to avoid the relative the path expansion in BuildConfiguration::buildDirectory.
@@ -48,6 +210,13 @@ public:
// ### Build Steps Clean ### // ### Build Steps Clean ###
appendInitialCleanStep(Constants::MAKE_STEP_ID); // make clean appendInitialCleanStep(Constants::MAKE_STEP_ID); // make clean
} }
~AutotoolsBuildConfiguration() override { delete m_buildSystem; }
private:
BuildSystem *buildSystem() const override { return m_buildSystem; }
AutotoolsBuildSystem * const m_buildSystem;
}; };
class AutotoolsBuildConfigurationFactory final : public BuildConfigurationFactory class AutotoolsBuildConfigurationFactory final : public BuildConfigurationFactory

View File

@@ -1,187 +0,0 @@
// Copyright (C) 2016 Openismus GmbH.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "autotoolsbuildsystem.h"
#include "makefileparser.h"
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/projectupdater.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtcppkitinfo.h>
#include <solutions/tasking/tasktreerunner.h>
#include <utils/async.h>
#include <utils/qtcassert.h>
using namespace ProjectExplorer;
using namespace Tasking;
using namespace Utils;
namespace AutotoolsProjectManager::Internal {
class AutotoolsBuildSystem final : public BuildSystem
{
public:
explicit AutotoolsBuildSystem(Target *target);
~AutotoolsBuildSystem() final;
private:
void triggerParsing() final;
QString name() const final { return QLatin1String("autotools"); }
/**
* Is invoked when the makefile parsing by m_makefileParserThread has
* been finished. Adds all sources and files into the project tree and
* takes care listen to file changes for Makefile.am and configure.ac
* files.
*/
void makefileParsingFinished(const MakefileParserOutputData &outputData);
/// Return value for AutotoolsProject::files()
QStringList m_files;
/// Responsible for parsing the makefiles asynchronously in a thread
Tasking::TaskTreeRunner m_parserRunner;
std::unique_ptr<ProjectUpdater> m_cppCodeModelUpdater;
};
AutotoolsBuildSystem::AutotoolsBuildSystem(Target *target)
: BuildSystem(target)
, m_cppCodeModelUpdater(ProjectUpdaterFactory::createCppProjectUpdater())
{
connect(target, &Target::activeBuildConfigurationChanged, this, [this] { requestParse(); });
connect(target->project(), &Project::projectFileIsDirty, this, [this] { requestParse(); });
}
AutotoolsBuildSystem::~AutotoolsBuildSystem() = default;
static void parseMakefileImpl(QPromise<MakefileParserOutputData> &promise, const QString &makefile)
{
const auto result = parseMakefile(makefile, QFuture<void>(promise.future()));
if (result)
promise.addResult(*result);
else
promise.future().cancel();
}
void AutotoolsBuildSystem::triggerParsing()
{
const Storage<std::optional<ParseGuard>> storage;
const auto onSetup = [this, storage](Async<MakefileParserOutputData> &async) {
*storage = guardParsingRun();
async.setConcurrentCallData(parseMakefileImpl, projectFilePath().path());
};
const auto onDone = [this, storage](const Async<MakefileParserOutputData> &async) {
(*storage)->markAsSuccess();
makefileParsingFinished(async.result());
};
const Group recipe {
storage,
AsyncTask<MakefileParserOutputData>(onSetup, onDone, CallDoneIf::Success)
};
m_parserRunner.start(recipe);
}
static QStringList filterIncludes(const QString &absSrc, const QString &absBuild,
const QStringList &in)
{
QStringList result;
for (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;
}
void AutotoolsBuildSystem::makefileParsingFinished(const MakefileParserOutputData &outputData)
{
m_files.clear();
QSet<FilePath> filesToWatch;
// Apply sources to m_files, which are returned at AutotoolsBuildSystem::files()
const QFileInfo fileInfo = projectFilePath().toFileInfo();
const QDir dir = fileInfo.absoluteDir();
const QStringList files = outputData.m_sources;
for (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 = outputData.m_makefiles;
for (const QString &makefile : makefiles) {
const QString absMakefile = dir.absoluteFilePath(makefile);
m_files.append(absMakefile);
filesToWatch.insert(FilePath::fromString(absMakefile));
}
// Add configure.ac file to project and watch for changes.
const QLatin1String configureAc(QLatin1String("configure.ac"));
const QFile configureAcFile(fileInfo.absolutePath() + QLatin1Char('/') + configureAc);
if (configureAcFile.exists()) {
const QString absConfigureAc = dir.absoluteFilePath(configureAc);
m_files.append(absConfigureAc);
filesToWatch.insert(FilePath::fromString(absConfigureAc));
}
auto newRoot = std::make_unique<ProjectNode>(project()->projectDirectory());
for (const QString &f : std::as_const(m_files)) {
const FilePath path = FilePath::fromString(f);
newRoot->addNestedNode(std::make_unique<FileNode>(path,
FileNode::fileTypeForFileName(path)));
}
setRootProjectNode(std::move(newRoot));
project()->setExtraProjectFiles(filesToWatch);
QtSupport::CppKitInfo kitInfo(kit());
QTC_ASSERT(kitInfo.isValid(), return );
RawProjectPart rpp;
rpp.setDisplayName(project()->displayName());
rpp.setProjectFileLocation(projectFilePath());
rpp.setQtVersion(kitInfo.projectPartQtVersion);
const QStringList cflags = outputData.m_cflags;
QStringList cxxflags = outputData.m_cxxflags;
if (cxxflags.isEmpty())
cxxflags = cflags;
const FilePath includeFileBaseDir = projectDirectory();
rpp.setFlagsForC({kitInfo.cToolchain, cflags, includeFileBaseDir});
rpp.setFlagsForCxx({kitInfo.cxxToolchain, cxxflags, includeFileBaseDir});
const QString absSrc = project()->projectDirectory().path();
BuildConfiguration *bc = target()->activeBuildConfiguration();
const QString absBuild = bc ? bc->buildDirectory().path() : QString();
rpp.setIncludePaths(filterIncludes(absSrc, absBuild, outputData.m_includePaths));
rpp.setMacros(outputData.m_macros);
rpp.setFiles(m_files);
m_cppCodeModelUpdater->update({project(), kitInfo, activeParseEnvironment(), {rpp}});
emitBuildSystemUpdated();
}
BuildSystem *createAutotoolsBuildSystem(Target *target)
{
return new AutotoolsBuildSystem(target);
}
} // AutotoolsProjectManager::Internal

View File

@@ -1,12 +0,0 @@
// Copyright (C) 2016 Openismus GmbH.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <projectexplorer/buildsystem.h>
namespace AutotoolsProjectManager::Internal {
ProjectExplorer::BuildSystem *createAutotoolsBuildSystem(ProjectExplorer::Target *target);
} // AutotoolsProjectManager::Internal

View File

@@ -17,8 +17,6 @@ QtcPlugin {
"autoreconfstep.h", "autoreconfstep.h",
"autotoolsbuildconfiguration.cpp", "autotoolsbuildconfiguration.cpp",
"autotoolsbuildconfiguration.h", "autotoolsbuildconfiguration.h",
"autotoolsbuildsystem.cpp",
"autotoolsbuildsystem.h",
"autotoolsprojectconstants.h", "autotoolsprojectconstants.h",
"autotoolsprojectmanagertr.h", "autotoolsprojectmanagertr.h",
"autotoolsprojectplugin.cpp", "autotoolsprojectplugin.cpp",

View File

@@ -4,7 +4,6 @@
#include "autogenstep.h" #include "autogenstep.h"
#include "autoreconfstep.h" #include "autoreconfstep.h"
#include "autotoolsbuildconfiguration.h" #include "autotoolsbuildconfiguration.h"
#include "autotoolsbuildsystem.h"
#include "autotoolsprojectconstants.h" #include "autotoolsprojectconstants.h"
#include "configurestep.h" #include "configurestep.h"
#include "makestep.h" #include "makestep.h"
@@ -41,7 +40,6 @@ public:
setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID));
setDisplayName(projectDirectory().fileName()); setDisplayName(projectDirectory().fileName());
setHasMakeInstallEquivalent(true); setHasMakeInstallEquivalent(true);
setBuildSystemCreator(&createAutotoolsBuildSystem);
} }
}; };