aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/cmakeprojectmanager/tealeafreader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cmakeprojectmanager/tealeafreader.cpp')
-rw-r--r--src/plugins/cmakeprojectmanager/tealeafreader.cpp528
1 files changed, 0 insertions, 528 deletions
diff --git a/src/plugins/cmakeprojectmanager/tealeafreader.cpp b/src/plugins/cmakeprojectmanager/tealeafreader.cpp
deleted file mode 100644
index 8b03b33f60..0000000000
--- a/src/plugins/cmakeprojectmanager/tealeafreader.cpp
+++ /dev/null
@@ -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;
-}
-
-QSet<FilePath> TeaLeafReader::projectFilesToWatch() const
-{
- return m_cmakeFiles;
-}
-
-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