forked from qt-creator/qt-creator
CMake: Remove tealeaf reader mode
*** This removes support for CMake < 3.7 *** Change-Id: Iddb0bab1a61b814adb4cb7ffa217284e96cb1140 Reviewed-by: Cristian Adam <cristian.adam@qt.io> Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -43,5 +43,4 @@ add_qtc_plugin(CMakeProjectManager
|
||||
projecttreehelper.cpp projecttreehelper.h
|
||||
servermode.cpp servermode.h
|
||||
servermodereader.cpp servermodereader.h
|
||||
tealeafreader.cpp tealeafreader.h
|
||||
)
|
||||
|
@@ -27,7 +27,6 @@
|
||||
|
||||
#include "fileapireader.h"
|
||||
#include "servermodereader.h"
|
||||
#include "tealeafreader.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
@@ -51,7 +50,7 @@ std::unique_ptr<BuildDirReader> BuildDirReader::createReader(const BuildDirParam
|
||||
case CMakeTool::ServerMode:
|
||||
return std::make_unique<ServerModeReader>();
|
||||
default:
|
||||
return std::make_unique<TeaLeafReader>();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -39,8 +39,7 @@ HEADERS = builddirmanager.h \
|
||||
fileapireader.h \
|
||||
projecttreehelper.h \
|
||||
servermode.h \
|
||||
servermodereader.h \
|
||||
tealeafreader.h
|
||||
servermodereader.h
|
||||
|
||||
SOURCES = builddirmanager.cpp \
|
||||
builddirparameters.cpp \
|
||||
@@ -77,8 +76,7 @@ SOURCES = builddirmanager.cpp \
|
||||
fileapireader.cpp \
|
||||
projecttreehelper.cpp \
|
||||
servermode.cpp \
|
||||
servermodereader.cpp \
|
||||
tealeafreader.cpp
|
||||
servermodereader.cpp
|
||||
|
||||
RESOURCES += cmakeproject.qrc
|
||||
|
||||
|
@@ -95,8 +95,6 @@ QtcPlugin {
|
||||
"servermode.cpp",
|
||||
"servermode.h",
|
||||
"servermodereader.cpp",
|
||||
"servermodereader.h",
|
||||
"tealeafreader.cpp",
|
||||
"tealeafreader.h"
|
||||
"servermodereader.h"
|
||||
]
|
||||
}
|
||||
|
@@ -110,6 +110,7 @@ public:
|
||||
, m_isAutoRun(item->isAutoRun())
|
||||
, m_autoCreateBuildDirectory(item->autoCreateBuildDirectory())
|
||||
, m_autodetected(item->isAutoDetected())
|
||||
, m_isSupported(item->hasFileApi() || item->hasServerMode())
|
||||
, m_changed(changed)
|
||||
{
|
||||
updateErrorFlags();
|
||||
@@ -186,6 +187,10 @@ public:
|
||||
error = QCoreApplication::translate(
|
||||
"CMakeProjectManager::Internal::CMakeToolTreeItem",
|
||||
"CMake executable path is not executable.");
|
||||
} else if (!m_isSupported) {
|
||||
error = QCoreApplication::translate(
|
||||
"CMakeProjectManager::Internal::CMakeToolTreeItem",
|
||||
"CMake executable does not provided required IDE integration features.");
|
||||
}
|
||||
if (result.isEmpty() || error.isEmpty())
|
||||
return QString("%1%2").arg(result).arg(error);
|
||||
@@ -216,6 +221,7 @@ public:
|
||||
bool m_pathIsExecutable = false;
|
||||
bool m_autoCreateBuildDirectory = false;
|
||||
bool m_autodetected = false;
|
||||
bool m_isSupported = false;
|
||||
bool m_changed = true;
|
||||
};
|
||||
|
||||
|
@@ -60,7 +60,6 @@ bool CMakeTool::Generator::matches(const QString &n, const QString &ex) const
|
||||
|
||||
namespace Internal {
|
||||
|
||||
const char READER_TYPE_TEALEAF[] = "tealeaf";
|
||||
const char READER_TYPE_SERVERMODE[] = "servermode";
|
||||
const char READER_TYPE_FILEAPI[] = "fileapi";
|
||||
|
||||
@@ -72,8 +71,6 @@ static bool ignoreFileApi()
|
||||
|
||||
static Utils::optional<CMakeTool::ReaderType> readerTypeFromString(const QString &input)
|
||||
{
|
||||
if (input == READER_TYPE_TEALEAF)
|
||||
return CMakeTool::TeaLeaf;
|
||||
if (input == READER_TYPE_SERVERMODE)
|
||||
return CMakeTool::ServerMode;
|
||||
if (input == READER_TYPE_FILEAPI)
|
||||
@@ -84,14 +81,12 @@ static Utils::optional<CMakeTool::ReaderType> readerTypeFromString(const QString
|
||||
static QString readerTypeToString(const CMakeTool::ReaderType &type)
|
||||
{
|
||||
switch (type) {
|
||||
case CMakeTool::TeaLeaf:
|
||||
return QString(READER_TYPE_TEALEAF);
|
||||
case CMakeTool::ServerMode:
|
||||
return QString(READER_TYPE_SERVERMODE);
|
||||
case CMakeTool::FileApi:
|
||||
return QString(READER_TYPE_FILEAPI);
|
||||
}
|
||||
return QString();
|
||||
return "<INVALID>";
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@@ -205,7 +200,7 @@ bool CMakeTool::isValid() const
|
||||
if (!m_introspection->m_didAttemptToRun)
|
||||
supportedGenerators();
|
||||
|
||||
return m_introspection->m_didRun;
|
||||
return m_introspection->m_didRun && (hasFileApi() || hasServerMode());
|
||||
}
|
||||
|
||||
Utils::SynchronousProcessResponse CMakeTool::run(const QStringList &args, int timeoutS) const
|
||||
@@ -386,7 +381,6 @@ CMakeTool::ReaderType CMakeTool::readerType() const
|
||||
}
|
||||
if (hasServerMode())
|
||||
return ServerMode;
|
||||
return TeaLeaf;
|
||||
}
|
||||
return m_readerType.value();
|
||||
}
|
||||
|
@@ -51,7 +51,7 @@ class CMAKE_EXPORT CMakeTool
|
||||
public:
|
||||
enum Detection { ManualDetection, AutoDetection };
|
||||
|
||||
enum ReaderType { TeaLeaf, ServerMode, FileApi };
|
||||
enum ReaderType { ServerMode, FileApi };
|
||||
|
||||
struct Version
|
||||
{
|
||||
|
@@ -1,528 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** 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 "tealeafreader.h"
|
||||
|
||||
#include "builddirmanager.h"
|
||||
#include "cmakebuildconfiguration.h"
|
||||
#include "cmakecbpparser.h"
|
||||
#include "cmakekitinformation.h"
|
||||
#include "cmakeprocess.h"
|
||||
#include "cmakeprojectconstants.h"
|
||||
#include "cmakeprojectnodes.h"
|
||||
|
||||
#include <coreplugin/documentmanager.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/idocument.h>
|
||||
#include <projectexplorer/headerpath.h>
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/target.h>
|
||||
#include <projectexplorer/toolchain.h>
|
||||
#include <projectexplorer/toolchainmanager.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// TeaLeafReader:
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
TeaLeafReader::TeaLeafReader()
|
||||
{
|
||||
connect(EditorManager::instance(), &EditorManager::aboutToSave,
|
||||
this, [this](const IDocument *document) {
|
||||
if (m_cmakeFiles.contains(document->filePath())
|
||||
|| !m_parameters.cmakeTool()
|
||||
|| !m_parameters.cmakeTool()->isAutoRun())
|
||||
emit dirty();
|
||||
});
|
||||
|
||||
// Remove \' (quote) for function-style macrosses:
|
||||
// -D'MACRO()'=xxx
|
||||
// -D'MACRO()=xxx'
|
||||
// -D'MACRO()'
|
||||
// otherwise, compiler will fails
|
||||
m_macroFixupRe1.setPattern("^-D(\\s*)'([0-9a-zA-Z_\\(\\)]+)'=");
|
||||
m_macroFixupRe2.setPattern("^-D(\\s*)'([0-9a-zA-Z_\\(\\)]+)=(.+)'$");
|
||||
m_macroFixupRe3.setPattern("^-D(\\s*)'([0-9a-zA-Z_\\(\\)]+)'$");
|
||||
}
|
||||
|
||||
TeaLeafReader::~TeaLeafReader()
|
||||
{
|
||||
stop();
|
||||
resetData();
|
||||
}
|
||||
|
||||
void TeaLeafReader::setParameters(const BuildDirParameters &p)
|
||||
{
|
||||
m_parameters = p;
|
||||
emit isReadyNow();
|
||||
}
|
||||
|
||||
bool TeaLeafReader::isCompatible(const BuildDirParameters &p)
|
||||
{
|
||||
return p.cmakeTool() && p.cmakeTool()->readerType() == CMakeTool::TeaLeaf;
|
||||
}
|
||||
|
||||
void TeaLeafReader::resetData()
|
||||
{
|
||||
m_projectName.clear();
|
||||
m_buildTargets.clear();
|
||||
m_files.clear();
|
||||
}
|
||||
|
||||
static QString findCbpFile(const QDir &directory)
|
||||
{
|
||||
// Find the cbp file
|
||||
// the cbp file is named like the project() command in the CMakeList.txt file
|
||||
// so this function below could find the wrong cbp file, if the user changes the project()
|
||||
// 2name
|
||||
QDateTime t;
|
||||
QString file;
|
||||
foreach (const QString &cbpFile , directory.entryList()) {
|
||||
if (cbpFile.endsWith(QLatin1String(".cbp"))) {
|
||||
QFileInfo fi(directory.path() + QLatin1Char('/') + cbpFile);
|
||||
if (t.isNull() || fi.lastModified() > t) {
|
||||
file = directory.path() + QLatin1Char('/') + cbpFile;
|
||||
t = fi.lastModified();
|
||||
}
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
void TeaLeafReader::parse(bool forceCMakeRun, bool forceConfiguration)
|
||||
{
|
||||
emit configurationStarted();
|
||||
|
||||
const QString cbpFile = findCbpFile(QDir(m_parameters.workDirectory.toString()));
|
||||
const QFileInfo cbpFileFi = cbpFile.isEmpty() ? QFileInfo() : QFileInfo(cbpFile);
|
||||
if (!cbpFileFi.exists() || forceConfiguration) {
|
||||
// Initial create:
|
||||
const FilePath path = m_parameters.workDirectory.pathAppended("qtcsettings.cmake");
|
||||
startCMake(QStringList({QString("-C"), path.toUserOutput()}));
|
||||
return;
|
||||
}
|
||||
|
||||
const bool mustUpdate = forceCMakeRun || m_cmakeFiles.isEmpty()
|
||||
|| anyOf(m_cmakeFiles, [&cbpFileFi](const FilePath &f) {
|
||||
return f.toFileInfo().lastModified() > cbpFileFi.lastModified();
|
||||
});
|
||||
if (mustUpdate) {
|
||||
startCMake(QStringList());
|
||||
} else {
|
||||
extractData();
|
||||
emit dataAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
void TeaLeafReader::stop()
|
||||
{
|
||||
m_cmakeProcess.reset();
|
||||
}
|
||||
|
||||
bool TeaLeafReader::isParsing() const
|
||||
{
|
||||
return m_cmakeProcess && m_cmakeProcess->state() != QProcess::NotRunning;
|
||||
}
|
||||
|
||||
QVector<FilePath> TeaLeafReader::takeProjectFilesToWatch()
|
||||
{
|
||||
return transform<QVector>(m_cmakeFiles, [](const FilePath &p) { return p; });
|
||||
}
|
||||
|
||||
QList<CMakeBuildTarget> TeaLeafReader::takeBuildTargets(QString &errorMessage)
|
||||
{
|
||||
Q_UNUSED(errorMessage)
|
||||
return m_buildTargets;
|
||||
}
|
||||
|
||||
CMakeConfig TeaLeafReader::takeParsedConfiguration(QString &errorMessage)
|
||||
{
|
||||
const FilePath cacheFile = m_parameters.workDirectory.pathAppended("CMakeCache.txt");
|
||||
|
||||
if (!cacheFile.exists())
|
||||
return { };
|
||||
|
||||
CMakeConfig result = BuildDirManager::parseCMakeConfiguration(cacheFile, &errorMessage);
|
||||
|
||||
if (!errorMessage.isEmpty()) {
|
||||
return { };
|
||||
}
|
||||
|
||||
const FilePath sourceOfBuildDir
|
||||
= FilePath::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", result));
|
||||
const FilePath canonicalSourceOfBuildDir = sourceOfBuildDir.canonicalPath();
|
||||
const FilePath canonicalSourceDirectory = m_parameters.sourceDirectory.canonicalPath();
|
||||
if (canonicalSourceOfBuildDir != canonicalSourceDirectory) { // Uses case-insensitive compare where appropriate
|
||||
errorMessage = tr("The build directory is not for %1 but for %2")
|
||||
.arg(canonicalSourceOfBuildDir.toUserOutput(),
|
||||
canonicalSourceDirectory.toUserOutput());
|
||||
return { };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<CMakeProjectNode> TeaLeafReader::generateProjectTree(
|
||||
const QList<const FileNode *> &allFiles, QString &errorMessage)
|
||||
{
|
||||
Q_UNUSED(errorMessage)
|
||||
if (m_files.size() == 0)
|
||||
return {};
|
||||
|
||||
auto root = std::make_unique<CMakeProjectNode>(m_parameters.sourceDirectory);
|
||||
root->setDisplayName(m_projectName);
|
||||
|
||||
QSet<FilePath> allIncludePathSet;
|
||||
for (const CMakeBuildTarget &bt : m_buildTargets) {
|
||||
const QList<Utils::FilePath> targetIncludePaths
|
||||
= Utils::filtered(bt.includeFiles, [this](const Utils::FilePath &fn) {
|
||||
return fn.isChildOf(m_parameters.sourceDirectory);
|
||||
});
|
||||
allIncludePathSet.unite(Utils::toSet(targetIncludePaths));
|
||||
}
|
||||
const QList<FilePath> allIncludePaths = Utils::toList(allIncludePathSet);
|
||||
|
||||
const QList<const FileNode *> missingHeaders
|
||||
= Utils::filtered(allFiles, [&allIncludePaths](const FileNode *fn) -> bool {
|
||||
if (fn->fileType() != FileType::Header)
|
||||
return false;
|
||||
|
||||
return Utils::contains(allIncludePaths, [fn](const FilePath &inc) { return fn->filePath().isChildOf(inc); });
|
||||
});
|
||||
|
||||
// filter duplicates:
|
||||
auto alreadySeen = Utils::transform<QSet>(m_files, &FileNode::filePath);
|
||||
const QList<const FileNode *> unseenMissingHeaders = Utils::filtered(missingHeaders, [&alreadySeen](const FileNode *fn) {
|
||||
const int count = alreadySeen.count();
|
||||
alreadySeen.insert(fn->filePath());
|
||||
return (alreadySeen.count() != count);
|
||||
});
|
||||
|
||||
root->addNestedNodes(std::move(m_files), m_parameters.sourceDirectory);
|
||||
|
||||
std::vector<std::unique_ptr<FileNode>> fileNodes
|
||||
= transform<std::vector>(unseenMissingHeaders, [](const FileNode *fn) {
|
||||
return std::unique_ptr<FileNode>(fn->clone());
|
||||
});
|
||||
root->addNestedNodes(std::move(fileNodes), m_parameters.sourceDirectory);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static void processCMakeIncludes(const CMakeBuildTarget &cbt, const ToolChain *tc,
|
||||
const QStringList& flags, const FilePath &sysroot,
|
||||
QSet<FilePath> &tcIncludes, QStringList &includePaths)
|
||||
{
|
||||
if (!tc)
|
||||
return;
|
||||
|
||||
foreach (const HeaderPath &hp, tc->builtInHeaderPaths(flags, sysroot,
|
||||
Environment::systemEnvironment())) {
|
||||
tcIncludes.insert(FilePath::fromString(hp.path));
|
||||
}
|
||||
foreach (const FilePath &i, cbt.includeFiles) {
|
||||
if (!tcIncludes.contains(i))
|
||||
includePaths.append(i.toString());
|
||||
}
|
||||
}
|
||||
|
||||
RawProjectParts TeaLeafReader::createRawProjectParts(QString &errorMessage)
|
||||
{
|
||||
Q_UNUSED(errorMessage)
|
||||
const ToolChain *tcCxx = ToolChainManager::findToolChain(m_parameters.cxxToolChainId);
|
||||
const ToolChain *tcC = ToolChainManager::findToolChain(m_parameters.cToolChainId);
|
||||
const FilePath sysroot = m_parameters.sysRoot;
|
||||
|
||||
RawProjectParts rpps;
|
||||
QHash<QString, QStringList> targetDataCacheCxx;
|
||||
QHash<QString, QStringList> targetDataCacheC;
|
||||
foreach (const CMakeBuildTarget &cbt, m_buildTargets) {
|
||||
if (cbt.targetType == UtilityType)
|
||||
continue;
|
||||
|
||||
// CMake shuffles the include paths that it reports via the CodeBlocks generator
|
||||
// So remove the toolchain include paths, so that at least those end up in the correct
|
||||
// place.
|
||||
auto cxxflags = getFlagsFor(cbt, targetDataCacheCxx, ProjectExplorer::Constants::CXX_LANGUAGE_ID);
|
||||
auto cflags = getFlagsFor(cbt, targetDataCacheC, ProjectExplorer::Constants::C_LANGUAGE_ID);
|
||||
QSet<FilePath> tcIncludes;
|
||||
QStringList includePaths;
|
||||
if (tcCxx || tcC) {
|
||||
processCMakeIncludes(cbt, tcCxx, cxxflags, sysroot, tcIncludes, includePaths);
|
||||
processCMakeIncludes(cbt, tcC, cflags, sysroot, tcIncludes, includePaths);
|
||||
} else {
|
||||
includePaths = transform(cbt.includeFiles, &FilePath::toString);
|
||||
}
|
||||
includePaths += m_parameters.workDirectory.toString();
|
||||
RawProjectPart rpp;
|
||||
rpp.setProjectFileLocation(cbt.sourceDirectory.toString() + "/CMakeLists.txt");
|
||||
rpp.setBuildSystemTarget(cbt.title);
|
||||
rpp.setIncludePaths(includePaths);
|
||||
|
||||
RawProjectPartFlags cProjectFlags;
|
||||
cProjectFlags.commandLineFlags = cflags;
|
||||
rpp.setFlagsForC(cProjectFlags);
|
||||
|
||||
RawProjectPartFlags cxxProjectFlags;
|
||||
cxxProjectFlags.commandLineFlags = cxxflags;
|
||||
rpp.setFlagsForCxx(cxxProjectFlags);
|
||||
|
||||
rpp.setMacros(cbt.macros);
|
||||
rpp.setDisplayName(cbt.title);
|
||||
rpp.setFiles(transform(cbt.files, &FilePath::toString));
|
||||
|
||||
const bool isExecutable = cbt.targetType == ExecutableType;
|
||||
rpp.setBuildTargetType(isExecutable ? ProjectExplorer::BuildTargetType::Executable
|
||||
: ProjectExplorer::BuildTargetType::Library);
|
||||
rpps.append(rpp);
|
||||
}
|
||||
|
||||
return rpps;
|
||||
}
|
||||
|
||||
void TeaLeafReader::extractData()
|
||||
{
|
||||
CMakeTool *cmake = m_parameters.cmakeTool();
|
||||
QTC_ASSERT(m_parameters.isValid() && cmake, return);
|
||||
|
||||
const FilePath srcDir = m_parameters.sourceDirectory;
|
||||
const FilePath bldDir = m_parameters.workDirectory;
|
||||
const FilePath topCMake = srcDir.pathAppended("CMakeLists.txt");
|
||||
|
||||
resetData();
|
||||
|
||||
m_projectName = m_parameters.projectName;
|
||||
m_files.emplace_back(std::make_unique<FileNode>(topCMake, FileType::Project));
|
||||
// Do not insert topCMake into m_cmakeFiles: The project already watches that!
|
||||
|
||||
// Find cbp file
|
||||
FilePath cbpFile = FilePath::fromString(findCbpFile(bldDir.toString()));
|
||||
if (cbpFile.isEmpty())
|
||||
return;
|
||||
m_cmakeFiles.insert(cbpFile);
|
||||
|
||||
// Add CMakeCache.txt file:
|
||||
const FilePath cacheFile = m_parameters.workDirectory.pathAppended("CMakeCache.txt");
|
||||
if (cacheFile.exists())
|
||||
m_cmakeFiles.insert(cacheFile);
|
||||
|
||||
// setFolderName
|
||||
CMakeCbpParser cbpparser;
|
||||
// Parsing
|
||||
if (!cbpparser.parseCbpFile(cmake->pathMapper(), cbpFile, srcDir))
|
||||
return;
|
||||
|
||||
m_projectName = cbpparser.projectName();
|
||||
|
||||
m_files = cbpparser.takeFileList();
|
||||
if (cbpparser.hasCMakeFiles()) {
|
||||
std::vector<std::unique_ptr<FileNode>> cmakeNodes = cbpparser.takeCmakeFileList();
|
||||
for (const std::unique_ptr<FileNode> &node : cmakeNodes)
|
||||
m_cmakeFiles.insert(node->filePath());
|
||||
|
||||
std::move(std::begin(cmakeNodes), std::end(cmakeNodes), std::back_inserter(m_files));
|
||||
}
|
||||
|
||||
// Make sure the top cmakelists.txt file is always listed:
|
||||
if (!contains(m_files, [topCMake](const std::unique_ptr<FileNode> &fn) {
|
||||
return fn->filePath() == topCMake;
|
||||
}))
|
||||
m_files.emplace_back(std::make_unique<FileNode>(topCMake, FileType::Project));
|
||||
|
||||
m_buildTargets = cbpparser.buildTargets();
|
||||
}
|
||||
|
||||
void TeaLeafReader::startCMake(const QStringList &configurationArguments)
|
||||
{
|
||||
QTC_ASSERT(!m_cmakeProcess, return);
|
||||
|
||||
m_cmakeProcess = std::make_unique<CMakeProcess>();
|
||||
|
||||
connect(m_cmakeProcess.get(), &CMakeProcess::finished,
|
||||
this, &TeaLeafReader::cmakeFinished);
|
||||
|
||||
m_cmakeProcess->run(m_parameters, configurationArguments);
|
||||
}
|
||||
|
||||
void TeaLeafReader::cmakeFinished(int code, QProcess::ExitStatus status)
|
||||
{
|
||||
Q_UNUSED(code)
|
||||
Q_UNUSED(status)
|
||||
|
||||
QTC_ASSERT(m_cmakeProcess, return);
|
||||
m_cmakeProcess.reset();
|
||||
|
||||
extractData(); // try even if cmake failed...
|
||||
|
||||
emit dataAvailable();
|
||||
}
|
||||
|
||||
QStringList TeaLeafReader::getFlagsFor(const CMakeBuildTarget &buildTarget,
|
||||
QHash<QString, QStringList> &cache,
|
||||
Id lang) const
|
||||
{
|
||||
// check cache:
|
||||
auto it = cache.constFind(buildTarget.title);
|
||||
if (it != cache.constEnd())
|
||||
return *it;
|
||||
|
||||
if (extractFlagsFromMake(buildTarget, cache, lang))
|
||||
return cache.value(buildTarget.title);
|
||||
|
||||
if (extractFlagsFromNinja(buildTarget, cache, lang))
|
||||
return cache.value(buildTarget.title);
|
||||
|
||||
cache.insert(buildTarget.title, QStringList());
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
bool TeaLeafReader::extractFlagsFromMake(const CMakeBuildTarget &buildTarget,
|
||||
QHash<QString, QStringList> &cache,
|
||||
Id lang) const
|
||||
{
|
||||
QString flagsPrefix;
|
||||
|
||||
if (lang == ProjectExplorer::Constants::CXX_LANGUAGE_ID)
|
||||
flagsPrefix = QLatin1String("CXX_FLAGS =");
|
||||
else if (lang == ProjectExplorer::Constants::C_LANGUAGE_ID)
|
||||
flagsPrefix = QLatin1String("C_FLAGS =");
|
||||
else
|
||||
return false;
|
||||
|
||||
QString makeCommand = buildTarget.makeCommand.toString();
|
||||
int startIndex = makeCommand.indexOf('\"');
|
||||
int endIndex = makeCommand.indexOf('\"', startIndex + 1);
|
||||
if (startIndex != -1 && endIndex != -1) {
|
||||
startIndex += 1;
|
||||
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
|
||||
int slashIndex = makefile.lastIndexOf('/');
|
||||
makefile.truncate(slashIndex);
|
||||
makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make");
|
||||
// Remove un-needed shell escaping:
|
||||
makefile = makefile.remove("\\");
|
||||
QFile file(makefile);
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return false;
|
||||
QTextStream stream(&file);
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().trimmed();
|
||||
if (line.startsWith(flagsPrefix)) {
|
||||
// Skip past =
|
||||
auto flags =
|
||||
Utils::transform(line.mid(flagsPrefix.length()).trimmed().split(' ', QString::SkipEmptyParts), [this](QString flag) -> QString {
|
||||
// TODO: maybe Gcc-specific
|
||||
// Remove \' (quote) for function-style macrosses:
|
||||
// -D'MACRO()'=xxx
|
||||
// -D'MACRO()=xxx'
|
||||
// -D'MACRO()'
|
||||
// otherwise, compiler will fails
|
||||
return flag
|
||||
.replace(m_macroFixupRe1, "-D\\1\\2=")
|
||||
.replace(m_macroFixupRe2, "-D\\1\\2=\\3")
|
||||
.replace(m_macroFixupRe3, "-D\\1\\2");
|
||||
});
|
||||
cache.insert(buildTarget.title, flags);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TeaLeafReader::extractFlagsFromNinja(const CMakeBuildTarget &buildTarget,
|
||||
QHash<QString, QStringList> &cache,
|
||||
Id lang) const
|
||||
{
|
||||
Q_UNUSED(buildTarget)
|
||||
if (!cache.isEmpty()) // We fill the cache in one go!
|
||||
return false;
|
||||
|
||||
QString compilerPrefix;
|
||||
if (lang == ProjectExplorer::Constants::CXX_LANGUAGE_ID)
|
||||
compilerPrefix = QLatin1String("CXX_COMPILER");
|
||||
else if (lang == ProjectExplorer::Constants::C_LANGUAGE_ID)
|
||||
compilerPrefix = QLatin1String("C_COMPILER");
|
||||
else
|
||||
return false;
|
||||
|
||||
// Attempt to find build.ninja file and obtain FLAGS (CXX_FLAGS/C_FLAGS) from there if no suitable flags.make were
|
||||
// found
|
||||
// Get "all" target's working directory
|
||||
QByteArray ninjaFile;
|
||||
QString buildNinjaFile = m_buildTargets.at(0).workingDirectory.toString();
|
||||
buildNinjaFile += "/build.ninja";
|
||||
QFile buildNinja(buildNinjaFile);
|
||||
if (buildNinja.exists()) {
|
||||
if (!buildNinja.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return false;
|
||||
ninjaFile = buildNinja.readAll();
|
||||
buildNinja.close();
|
||||
}
|
||||
|
||||
if (ninjaFile.isEmpty())
|
||||
return false;
|
||||
|
||||
QTextStream stream(ninjaFile);
|
||||
bool compilerFound = false;
|
||||
const QString targetSignature = "# Object build statements for ";
|
||||
QString currentTarget;
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
// 1. Look for a block that refers to the current target
|
||||
// 2. Look for a build rule which invokes CXX_COMPILER
|
||||
// 3. Return the FLAGS definition
|
||||
QString line = stream.readLine().trimmed();
|
||||
if (line.startsWith('#')) {
|
||||
if (line.startsWith(targetSignature)) {
|
||||
int pos = line.lastIndexOf(' ');
|
||||
currentTarget = line.mid(pos + 1);
|
||||
}
|
||||
} else if (!currentTarget.isEmpty() && line.startsWith("build")) {
|
||||
compilerFound = line.indexOf(compilerPrefix) != -1;
|
||||
} else if (compilerFound && line.startsWith("FLAGS =")) {
|
||||
// Skip past =
|
||||
cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts));
|
||||
}
|
||||
}
|
||||
return !cache.isEmpty();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CMakeProjectManager
|
@@ -1,92 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <projectexplorer/toolchain.h>
|
||||
|
||||
#include "builddirreader.h"
|
||||
#include "cmakeprocess.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace Utils { class QtcProcess; }
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class TeaLeafReader final : public BuildDirReader
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TeaLeafReader();
|
||||
~TeaLeafReader() final;
|
||||
|
||||
void setParameters(const BuildDirParameters &p) final;
|
||||
|
||||
bool isCompatible(const BuildDirParameters &p) final;
|
||||
void resetData() final;
|
||||
void parse(bool forceCMakeRun, bool forceConfiguration) final;
|
||||
void stop() final;
|
||||
|
||||
bool isParsing() const final;
|
||||
|
||||
QVector<Utils::FilePath> takeProjectFilesToWatch() final;
|
||||
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage) final;
|
||||
CMakeConfig takeParsedConfiguration(QString &errorMessage) final;
|
||||
std::unique_ptr<CMakeProjectNode> generateProjectTree(
|
||||
const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage) final;
|
||||
ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage) final;
|
||||
|
||||
private:
|
||||
void extractData();
|
||||
|
||||
void startCMake(const QStringList &configurationArguments);
|
||||
|
||||
void cmakeFinished(int code, QProcess::ExitStatus status);
|
||||
|
||||
QStringList getFlagsFor(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache, Core::Id lang) const;
|
||||
bool extractFlagsFromMake(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache, Core::Id lang) const;
|
||||
bool extractFlagsFromNinja(const CMakeBuildTarget &buildTarget, QHash<QString, QStringList> &cache, Core::Id lang) const;
|
||||
|
||||
// Process data:
|
||||
std::unique_ptr<CMakeProcess> m_cmakeProcess;
|
||||
|
||||
QSet<Utils::FilePath> m_cmakeFiles;
|
||||
QString m_projectName;
|
||||
QList<CMakeBuildTarget> m_buildTargets;
|
||||
std::vector<std::unique_ptr<ProjectExplorer::FileNode>> m_files;
|
||||
|
||||
// RegExps for function-like macrosses names fixups
|
||||
QRegularExpression m_macroFixupRe1;
|
||||
QRegularExpression m_macroFixupRe2;
|
||||
QRegularExpression m_macroFixupRe3;
|
||||
|
||||
friend class CMakeFile;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CMakeProjectManager
|
Reference in New Issue
Block a user