forked from qt-creator/qt-creator
CMake: De-noise CMakeProject
Change-Id: I9d5df01d0d4699df30c1f01c0cfe7f3da310457a Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -27,49 +27,35 @@
|
|||||||
|
|
||||||
#include "builddirmanager.h"
|
#include "builddirmanager.h"
|
||||||
#include "cmakebuildconfiguration.h"
|
#include "cmakebuildconfiguration.h"
|
||||||
#include "cmakebuildstep.h"
|
|
||||||
#include "cmakekitinformation.h"
|
#include "cmakekitinformation.h"
|
||||||
#include "cmakeprojectconstants.h"
|
#include "cmakeprojectconstants.h"
|
||||||
#include "cmakeprojectnodes.h"
|
#include "cmakeprojectnodes.h"
|
||||||
#include "cmakerunconfiguration.h"
|
#include "cmakerunconfiguration.h"
|
||||||
#include "cmakefile.h"
|
|
||||||
#include "cmakeprojectmanager.h"
|
#include "cmakeprojectmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
|
||||||
#include <coreplugin/documentmanager.h>
|
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
#include <cpptools/generatedcodemodelsupport.h>
|
||||||
#include <cpptools/projectinfo.h>
|
#include <cpptools/projectinfo.h>
|
||||||
#include <cpptools/projectpartbuilder.h>
|
#include <cpptools/projectpartbuilder.h>
|
||||||
#include <projectexplorer/buildsteplist.h>
|
|
||||||
#include <projectexplorer/buildtargetinfo.h>
|
#include <projectexplorer/buildtargetinfo.h>
|
||||||
#include <projectexplorer/customexecutablerunconfiguration.h>
|
|
||||||
#include <projectexplorer/deployconfiguration.h>
|
|
||||||
#include <projectexplorer/deploymentdata.h>
|
#include <projectexplorer/deploymentdata.h>
|
||||||
#include <projectexplorer/headerpath.h>
|
#include <projectexplorer/headerpath.h>
|
||||||
#include <projectexplorer/kitinformation.h>
|
#include <projectexplorer/kitinformation.h>
|
||||||
#include <projectexplorer/kitmanager.h>
|
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
#include <projectexplorer/toolchain.h>
|
#include <projectexplorer/toolchain.h>
|
||||||
#include <qtsupport/baseqtversion.h>
|
#include <qtsupport/baseqtversion.h>
|
||||||
#include <qtsupport/qtkitinformation.h>
|
#include <qtsupport/qtkitinformation.h>
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
#include <cpptools/generatedcodemodelsupport.h>
|
|
||||||
#include <cpptools/cppmodelmanager.h>
|
|
||||||
#include <cpptools/projectinfo.h>
|
|
||||||
#include <cpptools/projectpartbuilder.h>
|
|
||||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||||
#include <extensionsystem/pluginmanager.h>
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QTemporaryDir>
|
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
@@ -88,12 +74,12 @@ using namespace Internal;
|
|||||||
*/
|
*/
|
||||||
CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName)
|
CMakeProject::CMakeProject(CMakeManager *manager, const FileName &fileName)
|
||||||
{
|
{
|
||||||
setId(Constants::CMAKEPROJECT_ID);
|
setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID);
|
||||||
setProjectManager(manager);
|
setProjectManager(manager);
|
||||||
setDocument(new TextEditor::TextDocument);
|
setDocument(new TextEditor::TextDocument);
|
||||||
document()->setFilePath(fileName);
|
document()->setFilePath(fileName);
|
||||||
|
|
||||||
setRootProjectNode(new CMakeProjectNode(Utils::FileName::fromString(fileName.toFileInfo().absolutePath())));
|
setRootProjectNode(new CMakeProjectNode(FileName::fromString(fileName.toFileInfo().absolutePath())));
|
||||||
setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT));
|
setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT));
|
||||||
setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
|
setProjectLanguages(Core::Context(ProjectExplorer::Constants::LANG_CXX));
|
||||||
|
|
||||||
@@ -131,25 +117,24 @@ bool CMakeProject::extractCXXFlagsFromMake(const CMakeBuildTarget &buildTarget,
|
|||||||
QHash<QString, QStringList> &cache)
|
QHash<QString, QStringList> &cache)
|
||||||
{
|
{
|
||||||
QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand);
|
QString makeCommand = QDir::fromNativeSeparators(buildTarget.makeCommand);
|
||||||
int startIndex = makeCommand.indexOf(QLatin1Char('\"'));
|
int startIndex = makeCommand.indexOf('\"');
|
||||||
int endIndex = makeCommand.indexOf(QLatin1Char('\"'), startIndex + 1);
|
int endIndex = makeCommand.indexOf('\"', startIndex + 1);
|
||||||
if (startIndex != -1 && endIndex != -1) {
|
if (startIndex != -1 && endIndex != -1) {
|
||||||
startIndex += 1;
|
startIndex += 1;
|
||||||
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
|
QString makefile = makeCommand.mid(startIndex, endIndex - startIndex);
|
||||||
int slashIndex = makefile.lastIndexOf(QLatin1Char('/'));
|
int slashIndex = makefile.lastIndexOf('/');
|
||||||
makefile.truncate(slashIndex);
|
makefile.truncate(slashIndex);
|
||||||
makefile.append(QLatin1String("/CMakeFiles/") + buildTarget.title + QLatin1String(".dir/flags.make"));
|
makefile.append("/CMakeFiles/" + buildTarget.title + ".dir/flags.make");
|
||||||
QFile file(makefile);
|
QFile file(makefile);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
file.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
QTextStream stream(&file);
|
QTextStream stream(&file);
|
||||||
while (!stream.atEnd()) {
|
while (!stream.atEnd()) {
|
||||||
QString line = stream.readLine().trimmed();
|
QString line = stream.readLine().trimmed();
|
||||||
if (line.startsWith(QLatin1String("CXX_FLAGS ="))) {
|
if (line.startsWith("CXX_FLAGS =")) {
|
||||||
// Skip past =
|
// Skip past =
|
||||||
cache.insert(buildTarget.title,
|
cache.insert(buildTarget.title,
|
||||||
line.mid(11).trimmed().split(QLatin1Char(' '),
|
line.mid(11).trimmed().split(' ', QString::SkipEmptyParts));
|
||||||
QString::SkipEmptyParts));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +155,7 @@ bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
|
|||||||
// Get "all" target's working directory
|
// Get "all" target's working directory
|
||||||
QByteArray ninjaFile;
|
QByteArray ninjaFile;
|
||||||
QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory);
|
QString buildNinjaFile = QDir::fromNativeSeparators(buildTargets().at(0).workingDirectory);
|
||||||
buildNinjaFile += QLatin1String("/build.ninja");
|
buildNinjaFile += "/build.ninja";
|
||||||
QFile buildNinja(buildNinjaFile);
|
QFile buildNinja(buildNinjaFile);
|
||||||
if (buildNinja.exists()) {
|
if (buildNinja.exists()) {
|
||||||
buildNinja.open(QIODevice::ReadOnly | QIODevice::Text);
|
buildNinja.open(QIODevice::ReadOnly | QIODevice::Text);
|
||||||
@@ -183,7 +168,7 @@ bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
|
|||||||
|
|
||||||
QTextStream stream(ninjaFile);
|
QTextStream stream(ninjaFile);
|
||||||
bool cxxFound = false;
|
bool cxxFound = false;
|
||||||
const QString targetSignature = QLatin1String("# Object build statements for ");
|
const QString targetSignature = "# Object build statements for ";
|
||||||
QString currentTarget;
|
QString currentTarget;
|
||||||
|
|
||||||
while (!stream.atEnd()) {
|
while (!stream.atEnd()) {
|
||||||
@@ -191,16 +176,16 @@ bool CMakeProject::extractCXXFlagsFromNinja(const CMakeBuildTarget &buildTarget,
|
|||||||
// 2. Look for a build rule which invokes CXX_COMPILER
|
// 2. Look for a build rule which invokes CXX_COMPILER
|
||||||
// 3. Return the FLAGS definition
|
// 3. Return the FLAGS definition
|
||||||
QString line = stream.readLine().trimmed();
|
QString line = stream.readLine().trimmed();
|
||||||
if (line.startsWith(QLatin1Char('#'))) {
|
if (line.startsWith('#')) {
|
||||||
if (line.startsWith(targetSignature)) {
|
if (line.startsWith(targetSignature)) {
|
||||||
int pos = line.lastIndexOf(QLatin1Char(' '));
|
int pos = line.lastIndexOf(' ');
|
||||||
currentTarget = line.mid(pos + 1);
|
currentTarget = line.mid(pos + 1);
|
||||||
}
|
}
|
||||||
} else if (!currentTarget.isEmpty() && line.startsWith(QLatin1String("build"))) {
|
} else if (!currentTarget.isEmpty() && line.startsWith("build")) {
|
||||||
cxxFound = line.indexOf(QLatin1String("CXX_COMPILER")) != -1;
|
cxxFound = line.indexOf("CXX_COMPILER") != -1;
|
||||||
} else if (cxxFound && line.startsWith(QLatin1String("FLAGS ="))) {
|
} else if (cxxFound && line.startsWith("FLAGS =")) {
|
||||||
// Skip past =
|
// Skip past =
|
||||||
cache.insert(currentTarget, line.mid(7).trimmed().split(QLatin1Char(' '), QString::SkipEmptyParts));
|
cache.insert(currentTarget, line.mid(7).trimmed().split(' ', QString::SkipEmptyParts));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !cache.isEmpty();
|
return !cache.isEmpty();
|
||||||
@@ -226,7 +211,7 @@ void CMakeProject::updateProjectData()
|
|||||||
|
|
||||||
createGeneratedCodeModelSupport();
|
createGeneratedCodeModelSupport();
|
||||||
|
|
||||||
ToolChain *tc = ProjectExplorer::ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx);
|
ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx);
|
||||||
if (!tc) {
|
if (!tc) {
|
||||||
emit fileListChanged();
|
emit fileListChanged();
|
||||||
return;
|
return;
|
||||||
@@ -244,7 +229,7 @@ void CMakeProject::updateProjectData()
|
|||||||
activeQtVersion = CppTools::ProjectPart::Qt5;
|
activeQtVersion = CppTools::ProjectPart::Qt5;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Utils::FileName sysroot = ProjectExplorer::SysRootKitInformation::sysRoot(k);
|
const FileName sysroot = SysRootKitInformation::sysRoot(k);
|
||||||
|
|
||||||
ppBuilder.setQtVersion(activeQtVersion);
|
ppBuilder.setQtVersion(activeQtVersion);
|
||||||
|
|
||||||
@@ -377,17 +362,17 @@ QList<CMakeBuildTarget> CMakeProject::buildTargets() const
|
|||||||
QStringList CMakeProject::buildTargetTitles(bool runnable) const
|
QStringList CMakeProject::buildTargetTitles(bool runnable) const
|
||||||
{
|
{
|
||||||
const QList<CMakeBuildTarget> targets
|
const QList<CMakeBuildTarget> targets
|
||||||
= runnable ? Utils::filtered(buildTargets(),
|
= runnable ? filtered(buildTargets(),
|
||||||
[](const CMakeBuildTarget &ct) {
|
[](const CMakeBuildTarget &ct) {
|
||||||
return !ct.executable.isEmpty() && ct.targetType == ExecutableType;
|
return !ct.executable.isEmpty() && ct.targetType == ExecutableType;
|
||||||
})
|
})
|
||||||
: buildTargets();
|
: buildTargets();
|
||||||
return Utils::transform(targets, [](const CMakeBuildTarget &ct) { return ct.title; });
|
return transform(targets, [](const CMakeBuildTarget &ct) { return ct.title; });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMakeProject::hasBuildTarget(const QString &title) const
|
bool CMakeProject::hasBuildTarget(const QString &title) const
|
||||||
{
|
{
|
||||||
return Utils::anyOf(buildTargets(), [title](const CMakeBuildTarget &ct) { return ct.title == title; });
|
return anyOf(buildTargets(), [title](const CMakeBuildTarget &ct) { return ct.title == title; });
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CMakeProject::displayName() const
|
QString CMakeProject::displayName() const
|
||||||
@@ -397,21 +382,21 @@ QString CMakeProject::displayName() const
|
|||||||
|
|
||||||
QStringList CMakeProject::files(FilesMode fileMode) const
|
QStringList CMakeProject::files(FilesMode fileMode) const
|
||||||
{
|
{
|
||||||
const QList<FileNode *> nodes = Utils::filtered(rootProjectNode()->recursiveFileNodes(),
|
const QList<FileNode *> nodes = filtered(rootProjectNode()->recursiveFileNodes(),
|
||||||
[fileMode](const FileNode *fn) {
|
[fileMode](const FileNode *fn) {
|
||||||
const bool isGenerated = fn->isGenerated();
|
const bool isGenerated = fn->isGenerated();
|
||||||
switch (fileMode)
|
switch (fileMode)
|
||||||
{
|
{
|
||||||
case ProjectExplorer::Project::SourceFiles:
|
case Project::SourceFiles:
|
||||||
return !isGenerated;
|
return !isGenerated;
|
||||||
case ProjectExplorer::Project::GeneratedFiles:
|
case Project::GeneratedFiles:
|
||||||
return isGenerated;
|
return isGenerated;
|
||||||
case ProjectExplorer::Project::AllFiles:
|
case Project::AllFiles:
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return Utils::transform(nodes, [fileMode](const FileNode* fn) { return fn->filePath().toString(); });
|
return transform(nodes, [fileMode](const FileNode* fn) { return fn->filePath().toString(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *errorMessage)
|
Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *errorMessage)
|
||||||
@@ -495,7 +480,7 @@ QStringList CMakeProject::filesGeneratedFrom(const QString &sourceFile) const
|
|||||||
|
|
||||||
while (baseDirectory.isChildOf(project)) {
|
while (baseDirectory.isChildOf(project)) {
|
||||||
FileName cmakeListsTxt = baseDirectory;
|
FileName cmakeListsTxt = baseDirectory;
|
||||||
cmakeListsTxt.appendPath(QLatin1String("CMakeLists.txt"));
|
cmakeListsTxt.appendPath("CMakeLists.txt");
|
||||||
if (cmakeListsTxt.exists())
|
if (cmakeListsTxt.exists())
|
||||||
break;
|
break;
|
||||||
QDir dir(baseDirectory.toString());
|
QDir dir(baseDirectory.toString());
|
||||||
@@ -508,16 +493,16 @@ QStringList CMakeProject::filesGeneratedFrom(const QString &sourceFile) const
|
|||||||
QDir buildDir = QDir(activeTarget()->activeBuildConfiguration()->buildDirectory().toString());
|
QDir buildDir = QDir(activeTarget()->activeBuildConfiguration()->buildDirectory().toString());
|
||||||
QString generatedFilePath = buildDir.absoluteFilePath(relativePath);
|
QString generatedFilePath = buildDir.absoluteFilePath(relativePath);
|
||||||
|
|
||||||
if (fi.suffix() == QLatin1String("ui")) {
|
if (fi.suffix() == "ui") {
|
||||||
generatedFilePath += QLatin1String("/ui_");
|
generatedFilePath += "/ui_";
|
||||||
generatedFilePath += fi.completeBaseName();
|
generatedFilePath += fi.completeBaseName();
|
||||||
generatedFilePath += QLatin1String(".h");
|
generatedFilePath += ".h";
|
||||||
return QStringList(QDir::cleanPath(generatedFilePath));
|
return QStringList(QDir::cleanPath(generatedFilePath));
|
||||||
} else if (fi.suffix() == QLatin1String("scxml")) {
|
} else if (fi.suffix() == "scxml") {
|
||||||
generatedFilePath += QLatin1String("/");
|
generatedFilePath += "/";
|
||||||
generatedFilePath += QDir::cleanPath(fi.completeBaseName());
|
generatedFilePath += QDir::cleanPath(fi.completeBaseName());
|
||||||
return QStringList({generatedFilePath + QLatin1String(".h"),
|
return QStringList({ generatedFilePath + ".h",
|
||||||
generatedFilePath + QLatin1String(".cpp")});
|
generatedFilePath + ".cpp" });
|
||||||
} else {
|
} else {
|
||||||
// TODO: Other types will be added when adapters for their compilers become available.
|
// TODO: Other types will be added when adapters for their compilers become available.
|
||||||
return QStringList();
|
return QStringList();
|
||||||
@@ -566,15 +551,15 @@ void CMakeProject::updateApplicationAndDeploymentTargets()
|
|||||||
QDir sourceDir(t->project()->projectDirectory().toString());
|
QDir sourceDir(t->project()->projectDirectory().toString());
|
||||||
QDir buildDir(t->activeBuildConfiguration()->buildDirectory().toString());
|
QDir buildDir(t->activeBuildConfiguration()->buildDirectory().toString());
|
||||||
|
|
||||||
deploymentFile.setFileName(sourceDir.filePath(QLatin1String("QtCreatorDeployment.txt")));
|
deploymentFile.setFileName(sourceDir.filePath("QtCreatorDeployment.txt"));
|
||||||
// If we don't have a global QtCreatorDeployment.txt check for one created by the active build configuration
|
// If we don't have a global QtCreatorDeployment.txt check for one created by the active build configuration
|
||||||
if (!deploymentFile.exists())
|
if (!deploymentFile.exists())
|
||||||
deploymentFile.setFileName(buildDir.filePath(QLatin1String("QtCreatorDeployment.txt")));
|
deploymentFile.setFileName(buildDir.filePath("QtCreatorDeployment.txt"));
|
||||||
if (deploymentFile.open(QFile::ReadOnly | QFile::Text)) {
|
if (deploymentFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||||
deploymentStream.setDevice(&deploymentFile);
|
deploymentStream.setDevice(&deploymentFile);
|
||||||
deploymentPrefix = deploymentStream.readLine();
|
deploymentPrefix = deploymentStream.readLine();
|
||||||
if (!deploymentPrefix.endsWith(QLatin1Char('/')))
|
if (!deploymentPrefix.endsWith('/'))
|
||||||
deploymentPrefix.append(QLatin1Char('/'));
|
deploymentPrefix.append('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildTargetInfoList appTargetList;
|
BuildTargetInfoList appTargetList;
|
||||||
@@ -595,14 +580,14 @@ void CMakeProject::updateApplicationAndDeploymentTargets()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString absoluteSourcePath = sourceDir.absolutePath();
|
QString absoluteSourcePath = sourceDir.absolutePath();
|
||||||
if (!absoluteSourcePath.endsWith(QLatin1Char('/')))
|
if (!absoluteSourcePath.endsWith('/'))
|
||||||
absoluteSourcePath.append(QLatin1Char('/'));
|
absoluteSourcePath.append('/');
|
||||||
if (deploymentStream.device()) {
|
if (deploymentStream.device()) {
|
||||||
while (!deploymentStream.atEnd()) {
|
while (!deploymentStream.atEnd()) {
|
||||||
QString line = deploymentStream.readLine();
|
QString line = deploymentStream.readLine();
|
||||||
if (!line.contains(QLatin1Char(':')))
|
if (!line.contains(':'))
|
||||||
continue;
|
continue;
|
||||||
QStringList file = line.split(QLatin1Char(':'));
|
QStringList file = line.split(':');
|
||||||
deploymentData.addFile(absoluteSourcePath + file.at(0), deploymentPrefix + file.at(1));
|
deploymentData.addFile(absoluteSourcePath + file.at(0), deploymentPrefix + file.at(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -615,16 +600,16 @@ void CMakeProject::createGeneratedCodeModelSupport()
|
|||||||
{
|
{
|
||||||
qDeleteAll(m_extraCompilers);
|
qDeleteAll(m_extraCompilers);
|
||||||
m_extraCompilers.clear();
|
m_extraCompilers.clear();
|
||||||
QList<ProjectExplorer::ExtraCompilerFactory *> factories =
|
QList<ExtraCompilerFactory *> factories =
|
||||||
ProjectExplorer::ExtraCompilerFactory::extraCompilerFactories();
|
ExtraCompilerFactory::extraCompilerFactories();
|
||||||
|
|
||||||
// Find all files generated by any of the extra compilers, in a rather crude way.
|
// Find all files generated by any of the extra compilers, in a rather crude way.
|
||||||
foreach (const QString &file, files(SourceFiles)) {
|
foreach (const QString &file, files(SourceFiles)) {
|
||||||
foreach (ProjectExplorer::ExtraCompilerFactory *factory, factories) {
|
foreach (ExtraCompilerFactory *factory, factories) {
|
||||||
if (file.endsWith(QLatin1Char('.') + factory->sourceTag())) {
|
if (file.endsWith('.' + factory->sourceTag())) {
|
||||||
QStringList generated = filesGeneratedFrom(file);
|
QStringList generated = filesGeneratedFrom(file);
|
||||||
if (!generated.isEmpty()) {
|
if (!generated.isEmpty()) {
|
||||||
const FileNameList fileNames = Utils::transform(generated,
|
const FileNameList fileNames = transform(generated,
|
||||||
[](const QString &s) {
|
[](const QString &s) {
|
||||||
return FileName::fromString(s);
|
return FileName::fromString(s);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user