diff --git a/plugins/cmake/cmakebuilddirchooser.ui b/plugins/cmake/cmakebuilddirchooser.ui index d30da357e0..2d53b36ff7 100644 --- a/plugins/cmake/cmakebuilddirchooser.ui +++ b/plugins/cmake/cmakebuilddirchooser.ui @@ -1,196 +1,202 @@ CMakeBuildDirChooser 0 0 611 244 QFormLayout::ExpandingFieldsGrow 0 0 0 0 Available build directories: New build directory Build &directory: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter buildFolder &Installation prefix: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter installPrefix 0 0 + + The prefix under which the project will be installed. Leaving this empty will let CMake use its default install prefix. + + + CMake's default install prefix + Build &type: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter buildType true 0 Debug Release RelWithDebInfo MinSizeRel Extra arguments: 0 0 true CMake &executable: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter cmakeExecutable 0 0 Status Message... true KComboBox QComboBox
kcombobox.h
KUrlRequester QWidget
kurlrequester.h
diff --git a/plugins/cmake/cmakeutils.cpp b/plugins/cmake/cmakeutils.cpp index e88983a79b..70f57e822d 100644 --- a/plugins/cmake/cmakeutils.cpp +++ b/plugins/cmake/cmakeutils.cpp @@ -1,728 +1,722 @@ /* KDevelop CMake Support * * Copyright 2009 Andreas Pakulat * * 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 "cmakeutils.h" #include "cmakeprojectdata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icmakedocumentation.h" #include "cmakebuilddirchooser.h" #include "settings/cmakecachemodel.h" #include "debug.h" #include "cmakebuilderconfig.h" #include #include "parser/cmakelistsparser.h" using namespace KDevelop; namespace Config { namespace Old { static const QString currentBuildDirKey = QStringLiteral("CurrentBuildDir"); static const QString oldcmakeExecutableKey = QStringLiteral("CMake Binary"); // Todo: Remove at some point static const QString currentBuildTypeKey = QStringLiteral("CurrentBuildType"); static const QString currentInstallDirKey = QStringLiteral("CurrentInstallDir"); static const QString currentEnvironmentKey = QStringLiteral("CurrentEnvironment"); static const QString currentExtraArgumentsKey = QStringLiteral("Extra Arguments"); static const QString currentCMakeExecutableKey = QStringLiteral("Current CMake Binary"); static const QString projectRootRelativeKey = QStringLiteral("ProjectRootRelative"); static const QString projectBuildDirs = QStringLiteral("BuildDirs"); } static const QString buildDirIndexKey_ = QStringLiteral("Current Build Directory Index"); static const QString buildDirOverrideIndexKey = QStringLiteral("Temporary Build Directory Index"); static const QString buildDirCountKey = QStringLiteral("Build Directory Count"); //the used builddir will change for every runtime static QString buildDirIndexKey() { const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); return buildDirIndexKey_ + QLatin1Char('-') + currentRuntime; } namespace Specific { static const QString buildDirPathKey = QStringLiteral("Build Directory Path"); // TODO: migrate to more generic & consistent key term "CMake Executable" // Support the old "CMake Binary" key too for backwards compatibility during // a reasonable transition period. Both keys are saved at least until 5.2.0 // is released. Import support for the old key will need to remain for a // considably longer period, ideally. static const QString cmakeBinaryKey = QStringLiteral("CMake Binary"); static const QString cmakeExecutableKey = QStringLiteral("CMake Executable"); static const QString cmakeBuildTypeKey = QStringLiteral("Build Type"); static const QString cmakeInstallDirKey = QStringLiteral("Install Directory"); static const QString cmakeEnvironmentKey = QStringLiteral("Environment Profile"); static const QString cmakeArgumentsKey = QStringLiteral("Extra Arguments"); static const QString buildDirRuntime = QStringLiteral("Runtime"); } static const QString groupNameBuildDir = QStringLiteral("CMake Build Directory %1"); static const QString groupName = QStringLiteral("CMake"); } // namespace Config namespace { KConfigGroup baseGroup( KDevelop::IProject* project ) { if (!project) return KConfigGroup(); return project->projectConfiguration()->group( Config::groupName ); } KConfigGroup buildDirGroup( KDevelop::IProject* project, int buildDirIndex ) { return baseGroup(project).group( Config::groupNameBuildDir.arg(buildDirIndex) ); } bool buildDirGroupExists( KDevelop::IProject* project, int buildDirIndex ) { return baseGroup(project).hasGroup( Config::groupNameBuildDir.arg(buildDirIndex) ); } QString readBuildDirParameter( KDevelop::IProject* project, const QString& key, const QString& aDefault, int buildDirectory ) { const int buildDirIndex = buildDirectory<0 ? CMake::currentBuildDirIndex(project) : buildDirectory; if (buildDirIndex >= 0) return buildDirGroup( project, buildDirIndex ).readEntry( key, aDefault ); else return aDefault; } void writeBuildDirParameter( KDevelop::IProject* project, const QString& key, const QString& value ) { int buildDirIndex = CMake::currentBuildDirIndex(project); if (buildDirIndex >= 0) { KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex ); buildDirGrp.writeEntry( key, value ); } else { qCWarning(CMAKE) << "cannot write key" << key << "(" << value << ")" << "when no builddir is set!"; } } void writeProjectBaseParameter( KDevelop::IProject* project, const QString& key, const QString& value ) { KConfigGroup baseGrp = baseGroup(project); baseGrp.writeEntry( key, value ); } void setBuildDirRuntime( KDevelop::IProject* project, const QString& name) { writeBuildDirParameter(project, Config::Specific::buildDirRuntime, name); } QString buildDirRuntime( KDevelop::IProject* project, int builddir) { return readBuildDirParameter(project, Config::Specific::buildDirRuntime, QString(), builddir); } } // namespace namespace CMake { KDevelop::Path::List resolveSystemDirs(KDevelop::IProject* project, const QStringList& dirs) { const KDevelop::Path buildDir(CMake::currentBuildDir(project)); const KDevelop::Path installDir(CMake::currentInstallDir(project)); KDevelop::Path::List newList; newList.reserve(dirs.size()); for (const QString& s : dirs) { KDevelop::Path dir; if(s.startsWith(QLatin1String("#[bin_dir]"))) { dir = KDevelop::Path(buildDir, s); } else if(s.startsWith(QLatin1String("#[install_dir]"))) { dir = KDevelop::Path(installDir, s); } else { dir = KDevelop::Path(s); } // qCDebug(CMAKE) << "resolved" << s << "to" << d; if (!newList.contains(dir)) { newList.append(dir); } } return newList; } ///NOTE: when you change this, update @c defaultConfigure in cmakemanagertest.cpp bool checkForNeedingConfigure( KDevelop::IProject* project ) { const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); const KDevelop::Path builddir = currentBuildDir(project); const bool isValid = (buildDirRuntime(project, -1) == currentRuntime || buildDirRuntime(project, -1).isEmpty()) && builddir.isValid(); if( !isValid ) { CMakeBuildDirChooser bd; bd.setProject( project ); const auto builddirs = CMake::allBuildDirs(project); bd.setAlreadyUsed( builddirs ); bd.setShowAvailableBuildDirs(!builddirs.isEmpty()); bd.setCMakeExecutable(currentCMakeExecutable(project)); if( !bd.exec() ) { return false; } if (bd.reuseBuilddir()) { CMake::setCurrentBuildDirIndex( project, bd.alreadyUsedIndex() ); } else { int addedBuildDirIndex = buildDirCount( project ); // old count is the new index // Initialize the kconfig items with the values from the dialog, this ensures the settings // end up in the config file once the changes are saved qCDebug(CMAKE) << "adding to cmake config: new builddir index" << addedBuildDirIndex; qCDebug(CMAKE) << "adding to cmake config: builddir path " << bd.buildFolder(); qCDebug(CMAKE) << "adding to cmake config: installdir " << bd.installPrefix(); qCDebug(CMAKE) << "adding to cmake config: extra args" << bd.extraArguments(); qCDebug(CMAKE) << "adding to cmake config: build type " << bd.buildType(); qCDebug(CMAKE) << "adding to cmake config: cmake executable " << bd.cmakeExecutable(); qCDebug(CMAKE) << "adding to cmake config: environment "; CMake::setBuildDirCount( project, addedBuildDirIndex + 1 ); CMake::setCurrentBuildDirIndex( project, addedBuildDirIndex ); CMake::setCurrentBuildDir( project, bd.buildFolder() ); CMake::setCurrentInstallDir( project, bd.installPrefix() ); CMake::setCurrentExtraArguments( project, bd.extraArguments() ); CMake::setCurrentBuildType( project, bd.buildType() ); CMake::setCurrentCMakeExecutable(project, bd.cmakeExecutable()); CMake::setCurrentEnvironment( project, QString() ); } setBuildDirRuntime( project, currentRuntime ); return true; } else if( !QFile::exists( KDevelop::Path(builddir, QStringLiteral("CMakeCache.txt")).toLocalFile() ) || //TODO: maybe we could use the builder for that? !(QFile::exists( KDevelop::Path(builddir, QStringLiteral("Makefile")).toLocalFile() ) || QFile::exists( KDevelop::Path(builddir, QStringLiteral("build.ninja")).toLocalFile() ) ) ) { // User entered information already, but cmake hasn't actually been run yet. setBuildDirRuntime( project, currentRuntime ); return true; } setBuildDirRuntime( project, currentRuntime ); return false; } QHash enumerateTargets(const KDevelop::Path& targetsFilePath, const QString& sourceDir, const KDevelop::Path &buildDir) { const QString buildPath = buildDir.toLocalFile(); QHash targets; QFile targetsFile(targetsFilePath.toLocalFile()); if (!targetsFile.open(QIODevice::ReadOnly)) { qCDebug(CMAKE) << "Couldn't find the Targets file in" << targetsFile.fileName(); } QTextStream targetsFileStream(&targetsFile); const QRegularExpression rx(QStringLiteral("^(.*)/CMakeFiles/(.*).dir$")); while (!targetsFileStream.atEnd()) { const QString line = targetsFileStream.readLine(); auto match = rx.match(line); if (!match.isValid()) qCDebug(CMAKE) << "invalid match for" << line; const QString sourcePath = match.captured(1).replace(buildPath, sourceDir); targets[KDevelop::Path(sourcePath)].append(match.captured(2)); } return targets; } KDevelop::Path projectRoot(KDevelop::IProject* project) { if (!project) { return {}; } return project->path().cd(CMake::projectRootRelative(project)); } KDevelop::Path currentBuildDir( KDevelop::IProject* project, int builddir ) { return KDevelop::Path(readBuildDirParameter( project, Config::Specific::buildDirPathKey, QString(), builddir )); } KDevelop::Path commandsFile(KDevelop::IProject* project) { auto currentBuildDir = CMake::currentBuildDir(project); if (currentBuildDir.isEmpty()) { return {}; } return KDevelop::Path(currentBuildDir, QStringLiteral("compile_commands.json")); } KDevelop::Path targetDirectoriesFile(KDevelop::IProject* project) { auto currentBuildDir = CMake::currentBuildDir(project); if (currentBuildDir.isEmpty()) { return {}; } return KDevelop::Path(currentBuildDir, QStringLiteral("CMakeFiles/TargetDirectories.txt")); } QString currentBuildType( KDevelop::IProject* project, int builddir ) { return readBuildDirParameter( project, Config::Specific::cmakeBuildTypeKey, QStringLiteral("Release"), builddir ); } QString findExecutable() { auto cmake = QStandardPaths::findExecutable(QStringLiteral("cmake")); #ifdef Q_OS_WIN if (cmake.isEmpty()) cmake = QStandardPaths::findExecutable(QStringLiteral("cmake"), { QStringLiteral("C:\\Program Files (x86)\\CMake\\bin"), QStringLiteral("C:\\Program Files\\CMake\\bin"), QStringLiteral("C:\\Program Files (x86)\\CMake 2.8\\bin"), QStringLiteral("C:\\Program Files\\CMake 2.8\\bin")}); #endif return cmake; } KDevelop::Path currentCMakeExecutable(KDevelop::IProject* project, int builddir) { auto defaultCMakeExecutable = CMakeBuilderSettings::self()->cmakeExecutable().toLocalFile(); if (!QFileInfo::exists(ICore::self()->runtimeController()->currentRuntime()->pathInHost(KDevelop::Path(defaultCMakeExecutable)).toLocalFile())) defaultCMakeExecutable = CMake::findExecutable(); if (project) { // check for "CMake Executable" but for now also "CMake Binary", falling back to the default. auto projectCMakeExecutable = readBuildDirParameter( project, Config::Specific::cmakeExecutableKey, readBuildDirParameter( project, Config::Specific::cmakeBinaryKey, defaultCMakeExecutable, builddir), builddir ); if (projectCMakeExecutable != defaultCMakeExecutable) { QFileInfo info(projectCMakeExecutable); if (!info.isExecutable()) { projectCMakeExecutable = defaultCMakeExecutable; } } return KDevelop::Path(projectCMakeExecutable); } return KDevelop::Path(defaultCMakeExecutable); } KDevelop::Path currentInstallDir( KDevelop::IProject* project, int builddir ) { - const QString defaultInstallDir = -#ifdef Q_OS_WIN - QStringLiteral("C:\\Program Files"); -#else - QStringLiteral("/usr/local"); -#endif - return KDevelop::Path(readBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, defaultInstallDir, builddir )); + return KDevelop::Path(readBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, QString(), builddir )); } QString projectRootRelative( KDevelop::IProject* project ) { return baseGroup(project).readEntry( Config::Old::projectRootRelativeKey, "." ); } bool hasProjectRootRelative(KDevelop::IProject* project) { return baseGroup(project).hasKey( Config::Old::projectRootRelativeKey ); } QString currentExtraArguments( KDevelop::IProject* project, int builddir ) { return readBuildDirParameter( project, Config::Specific::cmakeArgumentsKey, QString(), builddir ); } void setCurrentInstallDir( KDevelop::IProject* project, const KDevelop::Path& path ) { writeBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, path.toLocalFile() ); } void setCurrentBuildType( KDevelop::IProject* project, const QString& type ) { writeBuildDirParameter( project, Config::Specific::cmakeBuildTypeKey, type ); } void setCurrentCMakeExecutable(KDevelop::IProject* project, const KDevelop::Path& path) { // maintain compatibility with older versions for now writeBuildDirParameter(project, Config::Specific::cmakeBinaryKey, path.toLocalFile()); writeBuildDirParameter(project, Config::Specific::cmakeExecutableKey, path.toLocalFile()); } void setCurrentBuildDir( KDevelop::IProject* project, const KDevelop::Path& path ) { writeBuildDirParameter( project, Config::Specific::buildDirPathKey, path.toLocalFile() ); } void setProjectRootRelative( KDevelop::IProject* project, const QString& relative) { writeProjectBaseParameter( project, Config::Old::projectRootRelativeKey, relative ); } void setCurrentExtraArguments( KDevelop::IProject* project, const QString& string) { writeBuildDirParameter( project, Config::Specific::cmakeArgumentsKey, string ); } QString currentEnvironment(KDevelop::IProject* project, int builddir) { return readBuildDirParameter( project, Config::Specific::cmakeEnvironmentKey, QString(), builddir ); } int currentBuildDirIndex( KDevelop::IProject* project ) { KConfigGroup baseGrp = baseGroup(project); if ( baseGrp.hasKey( Config::buildDirOverrideIndexKey ) ) return baseGrp.readEntry( Config::buildDirOverrideIndexKey, -1 ); else if (baseGrp.hasKey(Config::buildDirIndexKey())) return baseGrp.readEntry( Config::buildDirIndexKey(), -1 ); else return baseGrp.readEntry( Config::buildDirIndexKey_, -1 ); // backwards compatibility } void setCurrentBuildDirIndex( KDevelop::IProject* project, int buildDirIndex ) { writeProjectBaseParameter( project, Config::buildDirIndexKey(), QString::number (buildDirIndex) ); } void setCurrentEnvironment( KDevelop::IProject* project, const QString& environment ) { writeBuildDirParameter( project, Config::Specific::cmakeEnvironmentKey, environment ); } void initBuildDirConfig( KDevelop::IProject* project ) { int buildDirIndex = currentBuildDirIndex( project ); if (buildDirCount(project) <= buildDirIndex ) setBuildDirCount( project, buildDirIndex + 1 ); } int buildDirCount( KDevelop::IProject* project ) { return baseGroup(project).readEntry( Config::buildDirCountKey, 0 ); } void setBuildDirCount( KDevelop::IProject* project, int count ) { writeProjectBaseParameter( project, Config::buildDirCountKey, QString::number(count) ); } void removeBuildDirConfig( KDevelop::IProject* project ) { int buildDirIndex = currentBuildDirIndex( project ); if ( !buildDirGroupExists( project, buildDirIndex ) ) { qCWarning(CMAKE) << "build directory config" << buildDirIndex << "to be removed but does not exist"; return; } int bdCount = buildDirCount(project); setBuildDirCount( project, bdCount - 1 ); removeOverrideBuildDirIndex( project ); setCurrentBuildDirIndex( project, -1 ); // move (rename) the upper config groups to keep the numbering // if there's nothing to move, just delete the group physically if (buildDirIndex + 1 == bdCount) buildDirGroup( project, buildDirIndex ).deleteGroup(); else for (int i = buildDirIndex + 1; i < bdCount; ++i) { KConfigGroup src = buildDirGroup( project, i ); KConfigGroup dest = buildDirGroup( project, i - 1 ); dest.deleteGroup(); src.copyTo(&dest); src.deleteGroup(); } } QHash readCacheValues(const KDevelop::Path& cmakeCachePath, QSet variables) { QHash ret; QFile file(cmakeCachePath.toLocalFile()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(CMAKE) << "couldn't open CMakeCache.txt" << cmakeCachePath; return ret; } QTextStream in(&file); while (!in.atEnd() && !variables.isEmpty()) { QString line = in.readLine().trimmed(); if(!line.isEmpty() && line[0].isLetter()) { CacheLine c; c.readLine(line); if(!c.isCorrect()) continue; if (variables.remove(c.name())) { ret[c.name()] = c.value(); } } } return ret; } void updateConfig( KDevelop::IProject* project, int buildDirIndex) { if (buildDirIndex < 0) return; KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex ); const KDevelop::Path builddir(buildDirGrp.readEntry( Config::Specific::buildDirPathKey, QString() )); const KDevelop::Path cacheFilePath( builddir, QStringLiteral("CMakeCache.txt")); const QMap keys = { { QStringLiteral("CMAKE_COMMAND"), Config::Specific::cmakeExecutableKey }, { QStringLiteral("CMAKE_INSTALL_PREFIX"), Config::Specific::cmakeInstallDirKey }, { QStringLiteral("CMAKE_BUILD_TYPE"), Config::Specific::cmakeBuildTypeKey } }; const QHash cacheValues = readCacheValues(cacheFilePath, keys.keys().toSet()); for(auto it = cacheValues.constBegin(), itEnd = cacheValues.constEnd(); it!=itEnd; ++it) { const QString key = keys.value(it.key()); Q_ASSERT(!key.isEmpty()); // Use cache only when the config value is not set. Without this check we will always // overwrite values provided by the user in config dialog. if (buildDirGrp.readEntry(key).isEmpty() && !it.value().isEmpty()) { buildDirGrp.writeEntry( key, it.value() ); } } } void attemptMigrate( KDevelop::IProject* project ) { if ( !baseGroup(project).hasKey( Config::Old::projectBuildDirs ) ) { qCDebug(CMAKE) << "CMake settings migration: already done, exiting"; return; } KConfigGroup baseGrp = baseGroup(project); KDevelop::Path buildDir( baseGrp.readEntry( Config::Old::currentBuildDirKey, QString() ) ); int buildDirIndex = -1; const QStringList existingBuildDirs = baseGrp.readEntry( Config::Old::projectBuildDirs, QStringList() ); { // also, find current build directory in this list (we need an index, not path) QString currentBuildDirCanonicalPath = QDir( buildDir.toLocalFile() ).canonicalPath(); for( int i = 0; i < existingBuildDirs.count(); ++i ) { const QString& nextBuildDir = existingBuildDirs.at(i); if( QDir(nextBuildDir).canonicalPath() == currentBuildDirCanonicalPath ) { buildDirIndex = i; } } } int buildDirsCount = existingBuildDirs.count(); qCDebug(CMAKE) << "CMake settings migration: existing build directories" << existingBuildDirs; qCDebug(CMAKE) << "CMake settings migration: build directory count" << buildDirsCount; qCDebug(CMAKE) << "CMake settings migration: current build directory" << buildDir << "(index" << buildDirIndex << ")"; baseGrp.writeEntry( Config::buildDirCountKey, buildDirsCount ); baseGrp.writeEntry( Config::buildDirIndexKey(), buildDirIndex ); for (int i = 0; i < buildDirsCount; ++i) { qCDebug(CMAKE) << "CMake settings migration: writing group" << i << ": path" << existingBuildDirs.at(i); KConfigGroup buildDirGrp = buildDirGroup( project, i ); buildDirGrp.writeEntry( Config::Specific::buildDirPathKey, existingBuildDirs.at(i) ); } baseGrp.deleteEntry( Config::Old::currentBuildDirKey ); baseGrp.deleteEntry( Config::Old::currentCMakeExecutableKey ); baseGrp.deleteEntry( Config::Old::currentBuildTypeKey ); baseGrp.deleteEntry( Config::Old::currentInstallDirKey ); baseGrp.deleteEntry( Config::Old::currentEnvironmentKey ); baseGrp.deleteEntry( Config::Old::currentExtraArgumentsKey ); baseGrp.deleteEntry( Config::Old::projectBuildDirs ); } void setOverrideBuildDirIndex( KDevelop::IProject* project, int overrideBuildDirIndex ) { writeProjectBaseParameter( project, Config::buildDirOverrideIndexKey, QString::number(overrideBuildDirIndex) ); } void removeOverrideBuildDirIndex( KDevelop::IProject* project, bool writeToMainIndex ) { KConfigGroup baseGrp = baseGroup(project); if( !baseGrp.hasKey(Config::buildDirOverrideIndexKey) ) return; if( writeToMainIndex ) baseGrp.writeEntry( Config::buildDirIndexKey(), baseGrp.readEntry(Config::buildDirOverrideIndexKey) ); baseGrp.deleteEntry(Config::buildDirOverrideIndexKey); } ICMakeDocumentation* cmakeDocumentation() { return KDevelop::ICore::self()->pluginController()->extensionForPlugin(QStringLiteral("org.kdevelop.ICMakeDocumentation")); } QStringList allBuildDirs(KDevelop::IProject* project) { QStringList result; int bdCount = buildDirCount(project); result.reserve(bdCount); for (int i = 0; i < bdCount; ++i) result += buildDirGroup( project, i ).readEntry( Config::Specific::buildDirPathKey ); return result; } QString executeProcess(const QString& execName, const QStringList& args) { Q_ASSERT(!execName.isEmpty()); qCDebug(CMAKE) << "Executing:" << execName << "::" << args; QProcess p; QTemporaryDir tmp(QStringLiteral("kdevcmakemanager")); p.setWorkingDirectory( tmp.path() ); p.start(execName, args, QIODevice::ReadOnly); if(!p.waitForFinished()) { qCDebug(CMAKE) << "failed to execute:" << execName << args << p.exitStatus() << p.readAllStandardError(); } QByteArray b = p.readAllStandardOutput(); QString t = QString::fromUtf8(b.trimmed()); return t; } QStringList supportedGenerators() { QStringList generatorNames; bool hasNinja = ICore::self() && ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IProjectBuilder"), QStringLiteral("KDevNinjaBuilder")); if (hasNinja) generatorNames << QStringLiteral("Ninja"); #ifdef Q_OS_WIN // Visual Studio solution is the standard generator under windows, but we don't want to use // the VS IDE, so we need nmake makefiles generatorNames << QStringLiteral("NMake Makefiles") << QStringLiteral("MinGW Makefiles"); #endif generatorNames << QStringLiteral("Unix Makefiles"); return generatorNames; } QString defaultGenerator() { const QStringList generatorNames = supportedGenerators(); QString defGen = generatorNames.value(CMakeBuilderSettings::self()->generator()); if (defGen.isEmpty()) { qCWarning(CMAKE) << "Couldn't find builder with index " << CMakeBuilderSettings::self()->generator() << ", defaulting to 0"; CMakeBuilderSettings::self()->setGenerator(0); defGen = generatorNames.at(0); } return defGen; } QVector importTestSuites(const Path &buildDir) { const auto contents = CMakeListsParser::readCMakeFile(buildDir.toLocalFile() + QLatin1String("/CTestTestfile.cmake")); QVector tests; for (const auto& entry: contents) { if (entry.name == QLatin1String("add_test")) { auto args = entry.arguments; Test test; test.name = args.takeFirst().value; test.executable = args.takeFirst().value; test.arguments = kTransform(args, [](const CMakeFunctionArgument& arg) { return arg.value; }); tests += test; } else if (entry.name == QLatin1String("subdirs")) { tests += importTestSuites(Path(buildDir, entry.arguments.first().value)); } else if (entry.name == QLatin1String("set_tests_properties")) { if(entry.arguments.count() < 4 || entry.arguments.count() % 2) { qCWarning(CMAKE) << "found set_tests_properties() with unexpected number of arguments:" << entry.arguments.count(); continue; } if (tests.isEmpty() || entry.arguments.first().value != tests.last().name) { qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.first().value << " ...), but expected test " << tests.last().name; continue; } if (entry.arguments[1].value != QLatin1String("PROPERTIES")) { qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.first().value << entry.arguments.at(1).value << "...), but expected PROPERTIES as second argument"; continue; } Test &test = tests.last(); for (int i = 2; i < entry.arguments.count(); i += 2) test.properties[entry.arguments[i].value] = entry.arguments[i + 1].value; } } return tests; } } diff --git a/plugins/cmake/cmakeutils.h b/plugins/cmake/cmakeutils.h index 3642b94c21..ea2ac2509f 100644 --- a/plugins/cmake/cmakeutils.h +++ b/plugins/cmake/cmakeutils.h @@ -1,267 +1,268 @@ /* KDevelop CMake Support * * Copyright 2009 Andreas Pakulat * * 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. */ #ifndef CMAKEUTILS_H #define CMAKEUTILS_H #include "cmakecommonexport.h" #include #include #include namespace KDevelop { class ProjectBaseItem; class IProject; class Path; } struct Test; class ICMakeDocumentation; class CMakeCacheModel; template static T kTransform(const Q& list, W func) { T ret; ret.reserve(list.size()); for (auto it = list.constBegin(), itEnd = list.constEnd(); it!=itEnd; ++it) ret += func(*it); return ret; } template static int kIndexOf(const Q& list, W func) { int i = 0; for (auto it = list.constBegin(), itEnd = list.constEnd(); it!=itEnd; ++it) { if (func(*it)) return i; ++i; } return -1; } template static T kFilter(const Q &input, _UnaryOperation op) { T ret; for(const auto& v : input) { if (op(v)) ret += v; } return ret; } namespace CMake { /** * Checks whether there's a need to run cmake for the given project item * This is the case if no builddir has been specified, in which case * it asks for one. * * @returns true if configure should be run, false otherwise */ KDEVCMAKECOMMON_EXPORT bool checkForNeedingConfigure( KDevelop::IProject* project ); /** * @returns the current builddir for the given project or an empty url if none * has been set by the user. */ KDEVCMAKECOMMON_EXPORT KDevelop::Path currentBuildDir( KDevelop::IProject* project, int builddir = -1 ); /** * @returns the path to the 'compile_commands.json' file in the current builddir for the given project * or an empty url if none has been set by the user. */ KDEVCMAKECOMMON_EXPORT KDevelop::Path commandsFile( KDevelop::IProject* project ); /** * @returns the path to the 'CMakeFiles/TargetDirectories.txt' file in the current builddir for the given project * or an empty url if none has been set by the user. */ KDEVCMAKECOMMON_EXPORT KDevelop::Path targetDirectoriesFile( KDevelop::IProject* project ); /** * @returns the current build type for the given project or "Release" as default value. */ KDEVCMAKECOMMON_EXPORT QString currentBuildType( KDevelop::IProject* project, int builddir = -1 ); /** * @returns the CMake executable, taking into account standard * installation dirs on Windows, or empty string in case of failure. */ KDEVCMAKECOMMON_EXPORT QString findExecutable(); /** * @returns the current CMake executable for the given project, falling back to * CMakeBuilderSettings::self()->cmakeExecutable() as the user-specified KDevelop-wide default value. */ KDEVCMAKECOMMON_EXPORT KDevelop::Path currentCMakeExecutable(KDevelop::IProject* project, int builddir = -1); /** - * @returns the current install dir for the given project or "/usr/local" as default value. + * @returns the current install dir for the given project or an empty string if none has been defined + * (in that case, whatever default CMake sets for the platform will be used.) */ KDEVCMAKECOMMON_EXPORT KDevelop::Path currentInstallDir( KDevelop::IProject* project, int builddir = -1 ); /** * @returns the current extra arguments for the given project or "" as default value. */ KDEVCMAKECOMMON_EXPORT QString currentExtraArguments( KDevelop::IProject* project, int builddir = -1 ); /** * @returns the current build dir for the given project. */ KDEVCMAKECOMMON_EXPORT QString projectRootRelative( KDevelop::IProject* project ); /** * @returns whether there's projectRootRelative defined */ KDEVCMAKECOMMON_EXPORT bool hasProjectRootRelative( KDevelop::IProject* project ); /** * Extracts target names from builddir/CMakeFiles/TargetDirectories.txt and maps corresponding source locations to them. */ KDEVCMAKECOMMON_EXPORT QHash enumerateTargets(const KDevelop::Path& targetsFilePath, const QString& sourceDir, const KDevelop::Path &buildDir); /** * Convenience function to get the project root. */ KDEVCMAKECOMMON_EXPORT KDevelop::Path projectRoot( KDevelop::IProject* project ); /** * @returns the environment configuration for a @p project */ KDEVCMAKECOMMON_EXPORT QString currentEnvironment( KDevelop::IProject* project, int builddir = -1 ); /** * Sets the current install dir for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentInstallDir( KDevelop::IProject* project, const KDevelop::Path &path ); /** * Sets the current build type for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentBuildType( KDevelop::IProject* project, const QString& type ); /** * Sets the current CMake executable for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentCMakeExecutable(KDevelop::IProject* project, const KDevelop::Path& path); /** * Sets the current build dir for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentBuildDir( KDevelop::IProject* project, const KDevelop::Path& path ); /** * Sets the current build dir for the given project. */ KDEVCMAKECOMMON_EXPORT void setProjectRootRelative( KDevelop::IProject* project, const QString& path); /** * Sets the current extra arguments for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentExtraArguments( KDevelop::IProject* project, const QString& args ); /** * Obtains a cmake documentation instance if it exists */ KDEVCMAKECOMMON_EXPORT ICMakeDocumentation* cmakeDocumentation(); /** * Retrieves the configured build directories for @p project. */ KDEVCMAKECOMMON_EXPORT QStringList allBuildDirs(KDevelop::IProject* project); /** * Attempts to migrate the CMake configuration to per-builddir format. * Silently returns if the migration has already been performed. */ KDEVCMAKECOMMON_EXPORT void attemptMigrate( KDevelop::IProject* project ); /** * Attempts to update CMake configuration keys from the cache data (CMakeCache.txt) * * The model is created based on build directory path for the given index @p buildDirectory */ KDEVCMAKECOMMON_EXPORT void updateConfig( KDevelop::IProject* project, int buildDirectory); /** * Returns the current build directory count. */ KDEVCMAKECOMMON_EXPORT int buildDirCount( KDevelop::IProject* project ); /** * Sets the build directory count (equivalent to adding a new build directory). */ KDEVCMAKECOMMON_EXPORT void setBuildDirCount( KDevelop::IProject* project, int count ); /** * @returns the current builddir index for the given project or -1 if none * has been set by the user. */ KDEVCMAKECOMMON_EXPORT int currentBuildDirIndex( KDevelop::IProject *project ); /** * Sets the current build dir index for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentBuildDirIndex( KDevelop::IProject* project, int buildDirIndex ); /** * A hack to avoid adding an optional "build directory index" parameter to all functions here. * This function sets an alternate build directory index key that overrides regular build directory index. */ KDEVCMAKECOMMON_EXPORT void setOverrideBuildDirIndex( KDevelop::IProject* project, int overrideBuildDirIndex ); /** * This removes build directory override key (\ref setOverrideBuildDirIndex). * Silently returns if there is no override. * * @param writeToMainIndex Whether the overridden index should be saved to regular */ KDEVCMAKECOMMON_EXPORT void removeOverrideBuildDirIndex( KDevelop::IProject* project, bool writeToMainIndex = false ); /** * Sets the environment configuration for the given project. */ KDEVCMAKECOMMON_EXPORT void setCurrentEnvironment( KDevelop::IProject* project, const QString& environment ); /** * Removes current build directory (overridden or not) from the project configuration. * Override is then cleared and index set to -1. */ KDEVCMAKECOMMON_EXPORT void removeBuildDirConfig( KDevelop::IProject* project ); KDEVCMAKECOMMON_EXPORT KDevelop::Path::List resolveSystemDirs(KDevelop::IProject* project, const QStringList& dirs); /** Runs the process specified by @p execName with @p args */ KDEVCMAKECOMMON_EXPORT QString executeProcess(const QString& execName, const QStringList& args=QStringList()); /** Opens @p cmakeCachePath and parses it, returns a hash with the specified keys in @p variables */ KDEVCMAKECOMMON_EXPORT QHash readCacheValues(const KDevelop::Path& cmakeCachePath, QSet variables); KDEVCMAKECOMMON_EXPORT QStringList supportedGenerators(); KDEVCMAKECOMMON_EXPORT QString defaultGenerator(); KDEVCMAKECOMMON_EXPORT QVector importTestSuites(const KDevelop::Path &buildDir); } #endif