aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/mesonprojectmanager/project/mesonprojectparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/mesonprojectmanager/project/mesonprojectparser.cpp')
-rw-r--r--src/plugins/mesonprojectmanager/project/mesonprojectparser.cpp355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/plugins/mesonprojectmanager/project/mesonprojectparser.cpp b/src/plugins/mesonprojectmanager/project/mesonprojectparser.cpp
new file mode 100644
index 0000000000..6161659a65
--- /dev/null
+++ b/src/plugins/mesonprojectmanager/project/mesonprojectparser.cpp
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Alexis Jeandet.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** 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
+** 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.
+**
+** 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.
+**
+****************************************************************************/
+#include "mesonprojectparser.h"
+#include "projecttree/mesonprojectnodes.h"
+#include "projecttree/projecttree.h"
+#include <exewrappers/mesontools.h>
+#include <mesoninfoparser/mesoninfoparser.h>
+
+#include <coreplugin/messagemanager.h>
+#include <projectexplorer/projectexplorer.h>
+#include <utils/fileinprojectfinder.h>
+#include <utils/optional.h>
+#include <utils/runextensions.h>
+
+#include <QStringList>
+#include <QTextStream>
+
+namespace MesonProjectManager {
+namespace Internal {
+
+struct CompilerArgs
+{
+ QStringList args;
+ QStringList includePaths;
+ ProjectExplorer::Macros macros;
+};
+
+inline Utils::optional<QString> extractValueIfMatches(const QString &arg,
+ const QStringList &candidates)
+{
+ for (const auto &flag : candidates) {
+ if (arg.startsWith(flag))
+ return arg.mid(flag.length());
+ }
+ return Utils::nullopt;
+}
+
+inline Utils::optional<QString> extractInclude(const QString &arg)
+{
+ return extractValueIfMatches(arg, {"-I", "/I", "-isystem", "-imsvc", "/imsvc"});
+}
+inline Utils::optional<ProjectExplorer::Macro> extractMacro(const QString &arg)
+{
+ auto define = extractValueIfMatches(arg, {"-D", "/D"});
+ if (define)
+ return ProjectExplorer::Macro::fromKeyValue(define->toLatin1());
+ auto undef = extractValueIfMatches(arg, {"-U", "/U"});
+ if (undef)
+ return ProjectExplorer::Macro(undef->toLatin1(), ProjectExplorer::MacroType::Undefine);
+ return Utils::nullopt;
+}
+
+CompilerArgs splitArgs(const QStringList &args)
+{
+ CompilerArgs splited;
+ std::for_each(std::cbegin(args), std::cend(args), [&splited](const QString &arg) {
+ auto inc = extractInclude(arg);
+ if (inc) {
+ splited.includePaths << *inc;
+ } else {
+ auto macro = extractMacro(arg);
+ if (macro) {
+ splited.macros << *macro;
+ } else {
+ splited.args << arg;
+ }
+ }
+ });
+ return splited;
+}
+
+QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList)
+{
+ QStringList allAbs;
+ std::transform(std::cbegin(pathList),
+ std::cend(pathList),
+ std::back_inserter(allAbs),
+ [refPath](const QString &path) {
+ if (Utils::FileUtils::isAbsolutePath(path))
+ return path;
+ return refPath.pathAppended(path).toString();
+ });
+ return allAbs;
+}
+
+MesonProjectParser::MesonProjectParser(const Utils::Id &meson,
+ Utils::Environment env,
+ ProjectExplorer::Project *project)
+ : m_env{env}
+ , m_meson{meson}
+ , m_projectName{project->displayName()}
+{
+ connect(&m_process, &MesonProcess::finished, this, &MesonProjectParser::processFinished);
+ connect(&m_process,
+ &MesonProcess::readyReadStandardOutput,
+ &m_outputParser,
+ &MesonOutputParser::readStdo);
+
+ // TODO re-think the way all BuildSystem/ProjectParser are tied
+ // I take project info here, I also take build and src dir later from
+ // functions args.
+ auto fileFinder = new Utils::FileInProjectFinder;
+ fileFinder->setProjectDirectory(project->projectDirectory());
+ fileFinder->setProjectFiles(project->files(ProjectExplorer::Project::AllFiles));
+ m_outputParser.setFileFinder(fileFinder);
+}
+
+void MesonProjectParser::setMesonTool(const Utils::Id &meson)
+{
+ m_meson = meson;
+}
+
+bool MesonProjectParser::configure(const Utils::FilePath &sourcePath,
+ const Utils::FilePath &buildPath,
+ const QStringList &args)
+{
+ m_introType = IntroDataType::file;
+ m_srcDir = sourcePath;
+ m_buildDir = buildPath;
+ m_outputParser.setSourceDirectory(sourcePath);
+ auto cmd = MesonTools::mesonWrapper(m_meson)->configure(sourcePath, buildPath, args);
+ // see comment near m_pendingCommands declaration
+ m_pendingCommands.enqueue(
+ std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath),
+ false));
+ return m_process.run(cmd, m_env, m_projectName);
+}
+
+bool MesonProjectParser::wipe(const Utils::FilePath &sourcePath,
+ const Utils::FilePath &buildPath,
+ const QStringList &args)
+{
+ return setup(sourcePath, buildPath, args, true);
+}
+
+bool MesonProjectParser::setup(const Utils::FilePath &sourcePath,
+ const Utils::FilePath &buildPath,
+ const QStringList &args,
+ bool forceWipe)
+{
+ m_introType = IntroDataType::file;
+ m_srcDir = sourcePath;
+ m_buildDir = buildPath;
+ m_outputParser.setSourceDirectory(sourcePath);
+ auto cmdArgs = args;
+ if (forceWipe || isSetup(buildPath))
+ cmdArgs << "--wipe";
+ auto cmd = MesonTools::mesonWrapper(m_meson)->setup(sourcePath, buildPath, cmdArgs);
+ return m_process.run(cmd, m_env, m_projectName);
+}
+
+bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath)
+{
+ m_srcDir = sourcePath;
+ m_buildDir = buildPath;
+ m_outputParser.setSourceDirectory(sourcePath);
+ if (!isSetup(buildPath)) {
+ return parse(sourcePath);
+ } else {
+ m_introType = IntroDataType::file;
+ return startParser();
+ }
+}
+
+bool MesonProjectParser::parse(const Utils::FilePath &sourcePath)
+{
+ m_srcDir = sourcePath;
+ m_introType = IntroDataType::stdo;
+ m_outputParser.setSourceDirectory(sourcePath);
+ return m_process.run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath),
+ m_env,
+ m_projectName,
+ true);
+}
+
+QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const
+{
+ QList<ProjectExplorer::BuildTargetInfo> apps;
+ std::for_each(std::cbegin(m_parserResult.targets),
+ std::cend(m_parserResult.targets),
+ [&apps, srcDir = m_srcDir](const Target &target) {
+ if (target.type == Target::Type::executable) {
+ ProjectExplorer::BuildTargetInfo bti;
+ bti.displayName = target.name;
+ bti.buildKey = Target::fullName(srcDir, target);
+ bti.displayNameUniquifier = bti.buildKey;
+ bti.targetFilePath = Utils::FilePath::fromString(target.fileName.first());
+ bti.workingDirectory
+ = Utils::FilePath::fromString(target.fileName.first()).absolutePath();
+ bti.projectFilePath = Utils::FilePath::fromString(target.definedIn);
+ bti.usesTerminal = true;
+ apps.append(bti);
+ }
+ });
+ return apps;
+}
+bool MesonProjectParser::startParser()
+{
+ m_parserFutureResult = Utils::runAsync(
+ ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(),
+ [process = &m_process,
+ introType = m_introType,
+ buildDir = m_buildDir.toString(),
+ srcDir = m_srcDir]() {
+ if (introType == IntroDataType::file) {
+ return extractParserResults(srcDir, MesonInfoParser::parse(buildDir));
+ } else {
+ return extractParserResults(srcDir, MesonInfoParser::parse(process->stdOut()));
+ }
+ });
+
+ Utils::onFinished(m_parserFutureResult, this, &MesonProjectParser::update);
+ return true;
+}
+
+MesonProjectParser::ParserData *MesonProjectParser::extractParserResults(
+ const Utils::FilePath &srcDir, MesonInfoParser::Result &&parserResult)
+{
+ auto rootNode = ProjectTree::buildTree(srcDir,
+ parserResult.targets,
+ parserResult.buildSystemFiles);
+ return new ParserData{std::move(parserResult), std::move(rootNode)};
+}
+
+void MesonProjectParser::addMissingTargets(QStringList &targetList)
+{
+ // Not all targets are listed in introspection data
+ for (const auto &target : additionalTargets()) {
+ if (!targetList.contains(target)) {
+ targetList.append(target);
+ }
+ }
+}
+
+void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> &data)
+{
+ auto parserData = data.result();
+ m_parserResult = std::move(parserData->data);
+ m_rootNode = std::move(parserData->rootNode);
+ m_targetsNames.clear();
+ for (const Target &target : m_parserResult.targets) {
+ m_targetsNames.push_back(Target::fullName(m_srcDir, target));
+ }
+ addMissingTargets(m_targetsNames);
+ m_targetsNames.sort();
+ delete data;
+ emit parsingCompleted(true);
+}
+
+ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart(
+ const Target &target,
+ const Target::SourceGroup &sources,
+ const ProjectExplorer::ToolChain *cxxToolChain,
+ const ProjectExplorer::ToolChain *cToolChain)
+{
+ ProjectExplorer::RawProjectPart part;
+ part.setDisplayName(target.name);
+ part.setBuildSystemTarget(Target::fullName(m_srcDir, target));
+ part.setFiles(sources.sources + sources.generatedSources);
+ auto flags = splitArgs(sources.parameters);
+ part.setMacros(flags.macros);
+ part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths));
+ part.setProjectFileLocation(target.definedIn);
+ if (sources.language == "cpp")
+ part.setFlagsForCxx({cxxToolChain, flags.args});
+ else if (sources.language == "c")
+ part.setFlagsForC({cToolChain, flags.args});
+ part.setQtVersion(m_qtVersion);
+ return part;
+}
+
+void MesonProjectParser::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ if (exitCode == 0 && exitStatus == QProcess::NormalExit) {
+ if (m_pendingCommands.isEmpty())
+ startParser();
+ else {
+ // see comment near m_pendingCommands declaration
+ std::tuple<Command, bool> args = m_pendingCommands.dequeue();
+ m_process.run(std::get<0>(args), m_env, m_projectName, std::get<1>(args));
+ }
+ } else {
+ if (m_introType == IntroDataType::stdo) {
+ auto data = m_process.stdErr();
+ Core::MessageManager::write(QString::fromLocal8Bit(data));
+ m_outputParser.readStdo(data);
+ }
+ emit parsingCompleted(false);
+ }
+}
+
+ProjectExplorer::RawProjectParts MesonProjectParser::buildProjectParts(
+ const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cToolChain)
+{
+ ProjectExplorer::RawProjectParts parts;
+ for_each_source_group(m_parserResult.targets,
+ [&parts,
+ &cxxToolChain,
+ &cToolChain,
+ this](const Target &target, const Target::SourceGroup &sourceList) {
+ parts.push_back(
+ buildRawPart(target, sourceList, cxxToolChain, cToolChain));
+ });
+ return parts;
+}
+
+bool sourceGroupMatchesKit(const KitData &kit, const Target::SourceGroup &group)
+{
+ if (group.language == "c")
+ return kit.cCompilerPath == group.compiler[0];
+ if (group.language == "cpp")
+ return kit.cxxCompilerPath == group.compiler[0];
+ return true;
+}
+
+bool MesonProjectParser::matchesKit(const KitData &kit)
+{
+ bool matches = true;
+ for_each_source_group(m_parserResult.targets,
+ [&matches, &kit](const Target &, const Target::SourceGroup &sourceGroup) {
+ matches = matches && sourceGroupMatchesKit(kit, sourceGroup);
+ });
+ return matches;
+}
+
+bool MesonProjectParser::usesSameMesonVersion(const Utils::FilePath &buildPath)
+{
+ auto info = MesonInfoParser::mesonInfo(buildPath.toString());
+ auto meson = MesonTools::mesonWrapper(m_meson);
+ return info && meson && info->mesonVersion == meson->version();
+}
+} // namespace Internal
+} // namespace MesonProjectManager