diff --git a/kdevplatform/qtcompat_p.h b/kdevplatform/qtcompat_p.h index 12024b2249..c71bbcefdd 100644 --- a/kdevplatform/qtcompat_p.h +++ b/kdevplatform/qtcompat_p.h @@ -1,64 +1,82 @@ /* Copyright (c) 2017 Kevin Funk +#include + #if QT_VERSION < QT_VERSION_CHECK(5,7,0) namespace QtPrivate { template struct QAddConst { typedef const T Type; }; } // this adds const to non-const objects (like std::as_const) template Q_DECL_CONSTEXPR typename QtPrivate::QAddConst::Type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } // prevent rvalue arguments: template void qAsConst(const T &&) Q_DECL_EQ_DELETE; #endif -#endif - // compat for Q_FALLTHROUGH #if QT_VERSION < QT_VERSION_CHECK(5,8,0) #if defined(__has_cpp_attribute) # if __has_cpp_attribute(fallthrough) # define Q_FALLTHROUGH() [[fallthrough]] # elif __has_cpp_attribute(clang::fallthrough) # define Q_FALLTHROUGH() [[clang::fallthrough]] # elif __has_cpp_attribute(gnu::fallthrough) # define Q_FALLTHROUGH() [[gnu::fallthrough]] # endif #endif #ifndef Q_FALLTHROUGH # if defined(__GNUC__) && !defined(__INTEL_COMPILER) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 700) # define Q_FALLTHROUGH() __attribute__((fallthrough)) # else # define Q_FALLTHROUGH() (void)0 # endif #endif #endif + +namespace QtCompat { +// TODO: Just use QDir::listSeparator once we depend on Qt 5.6 +Q_DECL_CONSTEXPR inline QChar listSeparator() Q_DECL_NOTHROW +{ +#if QT_VERSION < QT_VERSION_CHECK(5,6,0) +#ifdef Q_OS_WIN + return QLatin1Char(';'); +#else + return QLatin1Char(':'); +#endif +#else + return QDir::listSeparator(); +#endif +} +} + +#endif diff --git a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp index 7ddd44ebdf..597fb4f625 100644 --- a/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp +++ b/plugins/custom-definesandincludes/compilerprovider/compilerprovider.cpp @@ -1,251 +1,251 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "compilerprovider.h" -#include - +#include "debug.h" +#include "qtcompat_p.h" #include "compilerfactories.h" #include "settingsmanager.h" #include #include #include #include #include #include #include using namespace KDevelop; namespace { class NoCompiler : public ICompiler { public: NoCompiler(): ICompiler(i18n("None"), QString(), QString(), false) {} QHash< QString, QString > defines(const QString&) const override { return {}; } Path::List includes(const QString&) const override { return {}; } }; static CompilerPointer createDummyCompiler() { static CompilerPointer compiler(new NoCompiler()); return compiler; } ConfigEntry configForItem(KDevelop::ProjectBaseItem* item) { if(!item){ return ConfigEntry(); } const Path itemPath = item->path(); const Path rootDirectory = item->project()->path(); auto paths = SettingsManager::globalInstance()->readPaths(item->project()->projectConfiguration().data()); ConfigEntry config; Path closestPath; // find config entry closest to the requested item for (const auto& entry : paths) { auto configEntry = entry; Path targetDirectory = rootDirectory; targetDirectory.addPath(entry.path); if (targetDirectory == itemPath) { return configEntry; } if (targetDirectory.isParentOf(itemPath)) { if (config.path.isEmpty() || targetDirectory.segments().size() > closestPath.segments().size()) { config = configEntry; closestPath = targetDirectory; } } } return config; } } CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent ) : QObject( parent ) , m_settings(settings) { m_factories.append(CompilerFactoryPointer(new GccFactory())); m_factories.append(CompilerFactoryPointer(new ClangFactory())); #ifdef _WIN32 m_factories.append(CompilerFactoryPointer(new MsvcFactory())); #endif if (!QStandardPaths::findExecutable( QStringLiteral("clang") ).isEmpty()) { m_factories[1]->registerDefaultCompilers(this); } if (!QStandardPaths::findExecutable( QStringLiteral("gcc") ).isEmpty()) { m_factories[0]->registerDefaultCompilers(this); } #ifdef _WIN32 if (!QStandardPaths::findExecutable("cl.exe").isEmpty()) { m_factories[2]->registerDefaultCompilers(this); } #endif registerCompiler(createDummyCompiler()); retrieveUserDefinedCompilers(); connect(ICore::self()->runtimeController(), &IRuntimeController::currentRuntimeChanged, this, [this]() { m_defaultProvider.clear(); }); } CompilerProvider::~CompilerProvider() = default; QHash CompilerProvider::defines( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { languageType = Utils::languageType(item->path(), config.parserArguments.parseAmbiguousAsCPP); } // If called on files that we can't compile, return an empty set of defines. if (languageType == Utils::Other) { return {}; } return config.compiler->defines(languageType == Utils::C ? config.parserArguments.cArguments : config.parserArguments.cppArguments); } Path::List CompilerProvider::includes( ProjectBaseItem* item ) const { auto config = configForItem(item); auto languageType = Utils::Cpp; if (item) { languageType = Utils::languageType(item->path(), config.parserArguments.parseAmbiguousAsCPP); } // If called on files that we can't compile, return an empty set of includes. if (languageType == Utils::Other) { return {}; } return config.compiler->includes(languageType == Utils::C ? config.parserArguments.cArguments : config.parserArguments.cppArguments); } Path::List CompilerProvider::frameworkDirectories( ProjectBaseItem* /* item */ ) const { return {}; } IDefinesAndIncludesManager::Type CompilerProvider::type() const { return IDefinesAndIncludesManager::CompilerSpecific; } CompilerPointer CompilerProvider::defaultCompiler() const { if (m_defaultProvider) return m_defaultProvider; auto rt = ICore::self()->runtimeController()->currentRuntime(); - const auto path = QFile::decodeName(rt->getenv("PATH")).split(QDir::listSeparator()); + const auto path = QFile::decodeName(rt->getenv("PATH")).split(QtCompat::listSeparator()); for ( const CompilerPointer& compiler : m_compilers ) { const bool absolutePath = QDir::isAbsolutePath(compiler->path()); if ((absolutePath && QFileInfo::exists(rt->pathInHost(Path(compiler->path())).toLocalFile())) || QStandardPaths::findExecutable( compiler->path(), path).isEmpty() ) { continue; } m_defaultProvider = compiler; break; } if (!m_defaultProvider) m_defaultProvider = createDummyCompiler(); qCDebug(DEFINESANDINCLUDES) << "new default compiler" << rt->name() << m_defaultProvider->name() << m_defaultProvider->path(); return m_defaultProvider; } QVector< CompilerPointer > CompilerProvider::compilers() const { return m_compilers; } CompilerPointer CompilerProvider::compilerForItem( KDevelop::ProjectBaseItem* item ) const { auto compiler = configForItem(item).compiler; Q_ASSERT(compiler); return compiler; } bool CompilerProvider::registerCompiler(const CompilerPointer& compiler) { if (!compiler) { return false; } for(auto c: m_compilers){ if (c->name() == compiler->name()) { return false; } } m_compilers.append(compiler); return true; } void CompilerProvider::unregisterCompiler(const CompilerPointer& compiler) { if (!compiler->editable()) { return; } for (int i = 0; i < m_compilers.count(); i++) { if (m_compilers[i]->name() == compiler->name()) { m_compilers.remove(i); break; } } } QVector< CompilerFactoryPointer > CompilerProvider::compilerFactories() const { return m_factories; } void CompilerProvider::retrieveUserDefinedCompilers() { auto compilers = m_settings->userDefinedCompilers(); for (auto c : compilers) { registerCompiler(c); } } diff --git a/plugins/qmakemanager/qmakeconfig.cpp b/plugins/qmakemanager/qmakeconfig.cpp index 955200a117..5370c653d3 100644 --- a/plugins/qmakemanager/qmakeconfig.cpp +++ b/plugins/qmakemanager/qmakeconfig.cpp @@ -1,176 +1,165 @@ /* KDevelop QMake Support * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "qmakeconfig.h" +#include "qtcompat_p.h" + #include #include #include #include #include #include #include #include const char QMakeConfig::CONFIG_GROUP[] = "QMake_Builder"; // TODO: migrate to more generic & consistent key term "QMake_Executable" const char QMakeConfig::QMAKE_EXECUTABLE[] = "QMake_Binary"; const char QMakeConfig::BUILD_FOLDER[] = "Build_Folder"; const char QMakeConfig::INSTALL_PREFIX[] = "Install_Prefix"; const char QMakeConfig::EXTRA_ARGUMENTS[] = "Extra_Arguments"; const char QMakeConfig::BUILD_TYPE[] = "Build_Type"; const char QMakeConfig::ALL_BUILDS[] = "All_Builds"; -namespace { - -// TODO: Just use QDir::listSeparator once we depend on Qt 5.6 -Q_DECL_CONSTEXPR inline QChar listSeparator() Q_DECL_NOTHROW -{ -#ifdef Q_OS_WIN - return QLatin1Char(';'); -#else - return QLatin1Char(':'); -#endif -} -} - using namespace KDevelop; /// NOTE: KConfig is not thread safe QMutex s_buildDirMutex; bool QMakeConfig::isConfigured(const IProject* project) { QMutexLocker lock(&s_buildDirMutex); KConfigGroup cg(project->projectConfiguration(), CONFIG_GROUP); return cg.exists() && cg.hasKey(QMAKE_EXECUTABLE) && cg.hasKey(BUILD_FOLDER); } Path QMakeConfig::buildDirFromSrc(const IProject* project, const Path& srcDir) { QMutexLocker lock(&s_buildDirMutex); KConfigGroup cg(project->projectConfiguration(), QMakeConfig::CONFIG_GROUP); Path buildDir = Path(cg.readEntry(QMakeConfig::BUILD_FOLDER, QString())); lock.unlock(); if (buildDir.isValid()) { buildDir.addPath(project->path().relativePath(srcDir)); } return buildDir; } QString QMakeConfig::qmakeExecutable(const IProject* project) { QMutexLocker lock(&s_buildDirMutex); QString exe; if (project) { KSharedConfig::Ptr cfg = project->projectConfiguration(); KConfigGroup group(cfg.data(), CONFIG_GROUP); if (group.hasKey(QMAKE_EXECUTABLE)) { exe = group.readEntry(QMAKE_EXECUTABLE, QString()); QFileInfo info(exe); if (!info.exists() || !info.isExecutable()) { qCWarning(KDEV_QMAKE) << "bad QMake configured for project " << project->path().toUrl() << ":" << exe; exe.clear(); } } } if (exe.isEmpty()) { exe = QStandardPaths::findExecutable(QStringLiteral("qmake")); } if (exe.isEmpty()) { exe = QStandardPaths::findExecutable(QStringLiteral("qmake-qt5")); } if (exe.isEmpty()) { exe = QStandardPaths::findExecutable(QStringLiteral("qmake-qt4")); } Q_ASSERT(!exe.isEmpty()); return exe; } QHash QMakeConfig::queryQMake(const QString& qmakeExecutable, const QStringList& args) { QHash hash; KProcess p; p.setOutputChannelMode(KProcess::OnlyStdoutChannel); p << qmakeExecutable << QStringLiteral("-query") << args; const int rc = p.execute(); if (rc != 0) { qCWarning(KDEV_QMAKE) << "failed to execute qmake query " << p.program().join(QStringLiteral(" ")) << "return code was:" << rc; return QHash(); } // TODO: Qt 5.5: Use QTextStream::readLineInto QTextStream stream(&p); while (!stream.atEnd()) { const QString line = stream.readLine(); const int colon = line.indexOf(':'); if (colon == -1) { continue; } const auto key = line.left(colon); const auto value = line.mid(colon + 1); hash.insert(key, value); } qCDebug(KDEV_QMAKE) << "Ran qmake (" << p.program().join(QStringLiteral(" ")) << "), found:" << hash; return hash; } QString QMakeConfig::findBasicMkSpec(const QHash& qmakeVars) { QStringList paths; if (qmakeVars.contains(QStringLiteral("QMAKE_MKSPECS"))) { // qt4 - foreach (const QString& dir, qmakeVars["QMAKE_MKSPECS"].split(listSeparator())) { + foreach (const QString& dir, qmakeVars["QMAKE_MKSPECS"].split(QtCompat::listSeparator())) { paths << dir + "/default/qmake.conf"; } } else if (!qmakeVars.contains(QStringLiteral("QMAKE_MKSPECS")) && qmakeVars.contains(QStringLiteral("QMAKE_SPEC"))) { QString path; // qt5 doesn't have the MKSPECS nor default anymore // let's try to look up the mkspec path ourselves, // see QMakeEvaluator::updateMkspecPaths() in QMake source code as reference if (qmakeVars.contains(QStringLiteral("QT_HOST_DATA/src"))) { // >=qt5.2: since 0d463c05fc4f2e79e5a4e5a5382a1e6ed5d2615e (in Qt5 qtbase repository) // mkspecs are no longer copied to the build directory. // instead, we have to look them up in the source directory. // this commit also introduced the key 'QT_HOST_DATA/src' which we use here path = qmakeVars[QStringLiteral("QT_HOST_DATA/src")]; } else if (qmakeVars.contains(QStringLiteral("QT_HOST_DATA"))) { // cross compilation path = qmakeVars[QStringLiteral("QT_HOST_DATA")]; } else { Q_ASSERT(qmakeVars.contains("QT_INSTALL_PREFIX")); path = qmakeVars[QStringLiteral("QT_INSTALL_PREFIX")]; } path += "/mkspecs/" + qmakeVars[QStringLiteral("QMAKE_SPEC")] + "/qmake.conf"; paths << path; } foreach (const QString& path, paths) { QFileInfo fi(path); if (fi.exists()) return fi.absoluteFilePath(); } return QString(); }