/**************************************************************************** ** ** Copyright (C) 2018 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 "compilationdatabaseproject.h" #include "compilationdatabaseconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CompilationDatabaseProjectManager { namespace Internal { class DBProjectNode : public ProjectExplorer::ProjectNode { public: explicit DBProjectNode(const Utils::FileName &projectFilePath) : ProjectExplorer::ProjectNode(projectFilePath) {} }; static QStringList splitCommandLine(QString commandLine) { QStringList result; bool insideQuotes = false; // Remove escaped quotes. commandLine.replace("\\\"", "'"); for (const QString &part : commandLine.split(QRegularExpression("\""))) { if (insideQuotes) { const QString quotedPart = "\"" + part + "\""; if (result.last().endsWith("=")) result.last().append(quotedPart); else result.append(quotedPart); } else { // If 's' is outside quotes ... result.append(part.split(QRegularExpression("\\s+"), QString::SkipEmptyParts)); } insideQuotes = !insideQuotes; } return result; } static QString updatedPathFlag(const QString &pathStr, const QString &workingDir, const QString &originalFlag) { QString result = pathStr; if (!QDir(pathStr).exists() && QDir(workingDir + "/" + pathStr).exists()) { result = workingDir + "/" + pathStr; } if (originalFlag.startsWith("-I")) return "-I" + result; if (originalFlag.startsWith("-isystem")) return "-isystem" + result; return result; } static QStringList filteredFlags(const QStringList &flags, const QString &fileName, const QString &workingDir) { QStringList filtered; // Skip compiler call if present. bool skipNext = !flags.first().startsWith('-'); bool includePath = false; for (const QString &flag : flags) { if (skipNext) { skipNext = false; continue; } QString pathStr; if (includePath) { includePath = false; pathStr = flag; } else if ((flag.startsWith("-I") || flag.startsWith("-isystem")) && flag != "-I" && flag != "-isystem") { pathStr = flag.mid(flag.startsWith("-I") ? 2 : 8); } if (!pathStr.isEmpty()) { filtered.push_back(updatedPathFlag(pathStr, workingDir, flag)); continue; } if (flag == "-c" || flag == "-pedantic" || flag.startsWith("/") || flag.startsWith("-m") || flag.startsWith("-O") || flag.startsWith("-W") || flag.startsWith("-w") || flag.startsWith("--sysroot=")) { continue; } if (flag == "-target" || flag == "-triple" || flag == "-isysroot" || flag == "-isystem" || flag == "--sysroot") { skipNext = true; continue; } if (flag.endsWith(fileName)) continue; if (flag == "-I" || flag == "-isystem") includePath = true; filtered.push_back(flag); } return filtered; } static CppTools::RawProjectPart makeRawProjectPart(const Utils::FileName &projectFile, const QJsonObject &object, const QString &workingDir, const Utils::FileName &fileName) { QStringList flags; const QJsonArray arguments = object["arguments"].toArray(); if (arguments.isEmpty()) { flags = splitCommandLine(object["command"].toString()); } else { for (const QJsonValue &arg : arguments) flags.append(arg.toString()); } flags = filteredFlags(flags, fileName.fileName(), workingDir); CppTools::RawProjectPart rpp; rpp.setProjectFileLocation(projectFile.toString()); rpp.setBuildSystemTarget(workingDir); rpp.setDisplayName(fileName.fileName()); rpp.setFiles({fileName.toString()}); CppTools::RawProjectPartFlags cxxProjectFlags; cxxProjectFlags.commandLineFlags = flags; rpp.setFlagsForCxx(cxxProjectFlags); return rpp; } CompilationDatabaseProject::CompilationDatabaseProject(const Utils::FileName &projectFile) : Project(Constants::COMPILATIONDATABASEMIMETYPE, projectFile) , m_cppCodeModelUpdater(std::make_unique(this)) { setId(Constants::COMPILATIONDATABASEPROJECT_ID); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setDisplayName(projectDirectory().fileName()); connect(this, &Project::activeTargetChanged, [this, projectFile](ProjectExplorer::Target *target) { if (!target) return; ProjectExplorer::Kit *kit = target->kit(); if (!kit) return; auto toolchains = ProjectExplorer::ToolChainKitInformation::toolChains(kit); if (toolchains.isEmpty()) return; emitParsingStarted(); const QFuture future = ::Utils::runAsync([this, projectFile, kit, tc = toolchains.first()](){ QFile file(projectFilePath().toString()); if (!file.open(QIODevice::ReadOnly)) { emitParsingFinished(false); return; } const QJsonArray array = QJsonDocument::fromJson(file.readAll()).array(); auto root = std::make_unique(projectDirectory()); root->addNode(std::make_unique( projectFile, ProjectExplorer::FileType::Project, false)); auto headers = std::make_unique( Utils::FileName::fromString("Headers"), 0); auto sources = std::make_unique( Utils::FileName::fromString("Sources"), 0); CppTools::RawProjectParts rpps; for (const QJsonValue &element : array) { const QJsonObject object = element.toObject(); const QString workingDir = object["directory"].toString(); Utils::FileName fileName = Utils::FileName::fromString( QDir::fromNativeSeparators(object["file"].toString())); if (!fileName.exists()) { fileName = Utils::FileUtils::canonicalPath( Utils::FileName::fromString(workingDir + "/" + fileName.toString())); } const QString filePath = fileName.toString(); const CppTools::ProjectFile::Kind kind = CppTools::ProjectFile::classify(filePath); ProjectExplorer::FolderNode *parent = nullptr; ProjectExplorer::FileType type = ProjectExplorer::FileType::Unknown; if (CppTools::ProjectFile::isHeader(kind)) { parent = headers.get(); type = ProjectExplorer::FileType::Header; } else if (CppTools::ProjectFile::isSource(kind)) { parent = sources.get(); type = ProjectExplorer::FileType::Source; } else { parent = root.get(); } parent->addNode(std::make_unique( fileName, type, false)); rpps.append(makeRawProjectPart(projectFile, object, workingDir, fileName)); } root->addNode(std::move(headers)); root->addNode(std::move(sources)); setRootProjectNode(std::move(root)); CppTools::ToolChainInfo tcInfo; tcInfo.type = ProjectExplorer::Constants::COMPILATION_DATABASE_TOOLCHAIN_TYPEID; tcInfo.isMsvc2015ToolChain = tc->targetAbi().osFlavor() == ProjectExplorer::Abi::WindowsMsvc2015Flavor; tcInfo.wordWidth = tc->targetAbi().wordWidth(); tcInfo.targetTriple = tc->originalTargetTriple(); tcInfo.sysRootPath = ProjectExplorer::SysRootKitInformation::sysRoot(kit).toString(); tcInfo.headerPathsRunner = tc->createBuiltInHeaderPathsRunner(); tcInfo.macroInspectionRunner = tc->createMacroInspectionRunner(); m_cppCodeModelUpdater->update({this, tcInfo, tcInfo, rpps}); emitParsingFinished(true); }); m_parserWatcher.setFuture(future); }); } CompilationDatabaseProject::~CompilationDatabaseProject() { m_parserWatcher.cancel(); m_parserWatcher.waitForFinished(); } static TextEditor::TextDocument *createCompilationDatabaseDocument() { auto doc = new TextEditor::TextDocument; doc->setId(Constants::COMPILATIONDATABASEPROJECT_ID); doc->setMimeType(Constants::COMPILATIONDATABASEMIMETYPE); return doc; } CompilationDatabaseEditorFactory::CompilationDatabaseEditorFactory() { setId(Constants::COMPILATIONDATABASEPROJECT_ID); setDisplayName("Compilation Database"); addMimeType(Constants::COMPILATIONDATABASEMIMETYPE); setEditorCreator([]() { return new TextEditor::BaseTextEditor; }); setEditorWidgetCreator([]() { return new TextEditor::TextEditorWidget; }); setDocumentCreator(createCompilationDatabaseDocument); setUseGenericHighlighter(true); setCommentDefinition(Utils::CommentDefinition::HashStyle); setCodeFoldingSupported(true); } } // namespace Internal } // namespace CompilationDatabaseProjectManager