diff options
Diffstat (limited to 'src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp')
-rw-r--r-- | src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp | 361 |
1 files changed, 159 insertions, 202 deletions
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index fdc02d0533..84312316a3 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -25,24 +25,20 @@ #include "cmakebuildconfiguration.h" -#include "builddirmanager.h" +#include "cmakebuildsettingswidget.h" #include "cmakebuildstep.h" -#include "cmakeconfigitem.h" +#include "cmakebuildsystem.h" #include "cmakekitinformation.h" #include "cmakeprojectconstants.h" -#include "cmakebuildsettingswidget.h" #include <android/androidconstants.h> - -#include <coreplugin/icore.h> - +#include <projectexplorer/buildaspects.h> #include <projectexplorer/buildinfo.h> #include <projectexplorer/buildmanager.h> #include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kit.h> #include <projectexplorer/kitinformation.h> +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmacroexpander.h> #include <projectexplorer/target.h> @@ -50,12 +46,12 @@ #include <qtsupport/qtbuildaspects.h> #include <qtsupport/qtkitinformation.h> -#include <utils/algorithm.h> -#include <utils/mimetypes/mimedatabase.h> #include <utils/qtcassert.h> -#include <utils/qtcprocess.h> +#include <utils/stringutils.h> +#include <QDir> #include <QLoggingCategory> +#include <QMessageBox> using namespace ProjectExplorer; using namespace Utils; @@ -67,52 +63,94 @@ static Q_LOGGING_CATEGORY(cmakeBuildConfigurationLog, "qtc.cmake.bc", QtWarningM const char CONFIGURATION_KEY[] = "CMake.Configuration"; -CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) +// ----------------------------------------------------------------------------- +// Helper: +// ----------------------------------------------------------------------------- + +static QStringList defaultInitialCMakeArguments(const Kit *k, const QString buildType) +{ + // Generator: + QStringList initialArgs = CMakeGeneratorKitAspect::generatorArguments(k); + + // CMAKE_BUILD_TYPE: + if (!buildType.isEmpty()) + initialArgs.append(QString::fromLatin1("-DCMAKE_BUILD_TYPE:String=%1").arg(buildType)); + + // Cross-compilation settings: + const QString sysRoot = SysRootKitAspect::sysRoot(k).toString(); + if (!sysRoot.isEmpty()) { + initialArgs.append(QString::fromLatin1("-DCMAKE_SYSROOT:PATH=%1").arg(sysRoot)); + if (ToolChain *tc = ToolChainKitAspect::cxxToolChain(k)) { + const QString targetTriple = tc->originalTargetTriple(); + initialArgs.append( + QString::fromLatin1("-DCMAKE_C_COMPILER_TARGET:STRING=%1").arg(targetTriple)); + initialArgs.append( + QString::fromLatin1("-DCMAKE_CXX_COMPILER_TARGET:STRING=%1").arg(targetTriple)); + } + } + + initialArgs += CMakeConfigurationKitAspect::toArgumentsList(k); + + return initialArgs; +} + +// ----------------------------------------------------------------------------- +// CMakeBuildConfiguration: +// ----------------------------------------------------------------------------- + +CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Utils::Id id) : BuildConfiguration(target, id) { - setBuildDirectory(shadowBuildDirectory(project()->projectFilePath(), - target->kit(), - displayName(), - BuildConfiguration::Unknown)); + m_buildSystem = new CMakeBuildSystem(this); + + buildDirectoryAspect()->setFileDialogOnly(true); + const auto buildDirAspect = aspect<BuildDirectoryAspect>(); + buildDirAspect->setFileDialogOnly(true); + buildDirAspect->setValueAcceptor( + [](const QString &oldDir, const QString &newDir) -> Utils::optional<QString> { + if (oldDir.isEmpty()) + return newDir; + + if (QDir(oldDir).exists("CMakeCache.txt")) { + if (QMessageBox::information(nullptr, + tr("Changing Build Directory"), + tr("Change the build directory and start with a " + "basic CMake configuration?"), + QMessageBox::Ok, + QMessageBox::Cancel) + == QMessageBox::Ok) { + return newDir; + } + return Utils::nullopt; + } + return newDir; + }); + + addAspect<InitialCMakeArgumentsAspect>(); appendInitialBuildStep(Constants::CMAKE_BUILD_STEP_ID); appendInitialCleanStep(Constants::CMAKE_BUILD_STEP_ID); setInitializer([this, target](const BuildInfo &info) { + const Kit *k = target->kit(); - CMakeConfig config; - config.append({"CMAKE_BUILD_TYPE", info.typeName.toUtf8()}); - - Kit *k = target->kit(); - const QString sysRoot = SysRootKitAspect::sysRoot(k).toString(); - if (!sysRoot.isEmpty()) { - config.append(CMakeConfigItem("CMAKE_SYSROOT", sysRoot.toUtf8())); - ToolChain *tc = ToolChainKitAspect::toolChain(k, - ProjectExplorer::Constants::CXX_LANGUAGE_ID); - if (tc) { - const QByteArray targetTriple = tc->originalTargetTriple().toUtf8(); - config.append(CMakeConfigItem("CMAKE_C_COMPILER_TARGET", targetTriple)); - config.append(CMakeConfigItem("CMAKE_CXX_COMPILER_TARGET ", targetTriple)); - } - } + QStringList initialArgs = defaultInitialCMakeArguments(k, info.typeName); + // Android magic: if (DeviceTypeKitAspect::deviceTypeId(k) == Android::Constants::ANDROID_DEVICE_TYPE) { buildSteps()->appendStep(Android::Constants::ANDROID_BUILD_APK_ID); const auto &bs = buildSteps()->steps().constLast(); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_NATIVE_API_LEVEL", - CMakeProjectManager::CMakeConfigItem::Type::STRING, - "Android native API level", - bs->data(Android::Constants::AndroidNdkPlatform).toString().toUtf8()}); + initialArgs.append( + QString::fromLatin1("-DANDROID_NATIVE_API_LEVEL:STRING=%1") + .arg(bs->data(Android::Constants::AndroidNdkPlatform).toString())); auto ndkLocation = bs->data(Android::Constants::NdkLocation).value<FilePath>(); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_NDK", - CMakeProjectManager::CMakeConfigItem::Type::PATH, - "Android NDK PATH", - ndkLocation.toString().toUtf8()}); + initialArgs.append( + QString::fromLatin1("-DANDROID_NDK:PATH=%1").arg(ndkLocation.toString())); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_TOOLCHAIN_FILE", - CMakeProjectManager::CMakeConfigItem::Type::PATH, - "Android CMake toolchain file", - ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").toString().toUtf8()}); + initialArgs.append( + QString::fromLatin1("-DCMAKE_TOOLCHAIN_FILE:PATH=%1") + .arg( + ndkLocation.pathAppended("build/cmake/android.toolchain.cmake").toString())); auto androidAbis = bs->data(Android::Constants::AndroidABIs).toStringList(); QString preferredAbi; @@ -123,29 +161,19 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) } else { preferredAbi = androidAbis.first(); } - // FIXME: configmodel doesn't care about our androidAbis list... - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_ABI", - CMakeProjectManager::CMakeConfigItem::Type::STRING, - "Android ABI", - preferredAbi.toLatin1(), - androidAbis}); + initialArgs.append(QString::fromLatin1("-DANDROID_ABI:STRING=%1").arg(preferredAbi)); QtSupport::BaseQtVersion *qt = QtSupport::QtKitAspect::qtVersion(k); if (qt->qtVersion() >= QtSupport::QtVersionNumber{5, 14, 0}) { auto sdkLocation = bs->data(Android::Constants::SdkLocation).value<FilePath>(); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_SDK", - CMakeProjectManager::CMakeConfigItem::Type::PATH, - "Android SDK PATH", - sdkLocation.toString().toUtf8()}); - + initialArgs.append( + QString::fromLatin1("-DANDROID_SDK:PATH=%1").arg(sdkLocation.toString())); } - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"ANDROID_STL", - CMakeProjectManager::CMakeConfigItem::Type::STRING, - "Android STL", - "c++_shared"}); + initialArgs.append(QString::fromLatin1("-DANDROID_STL:STRING=cxx_shared")); - m_initialConfiguration.prepend(CMakeProjectManager::CMakeConfigItem{"CMAKE_FIND_ROOT_PATH", "%{Qt:QT_INSTALL_PREFIX}"}); + initialArgs.append( + QString::fromLatin1("-DCMAKE_FIND_ROOT_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}")); } if (info.buildDirectory.isEmpty()) { @@ -155,22 +183,11 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Core::Id id) info.buildType)); } - setConfigurationForCMake(config); - - // Only do this after everything has been set up! - m_buildSystem = new CMakeBuildSystem(this); + setInitialCMakeArguments(initialArgs); }); const auto qmlDebuggingAspect = addAspect<QtSupport::QmlDebuggingAspect>(); qmlDebuggingAspect->setKit(target->kit()); - connect(qmlDebuggingAspect, &QtSupport::QmlDebuggingAspect::changed, - this, &CMakeBuildConfiguration::configurationForCMakeChanged); - - // m_buildSystem is still nullptr here since it the build directory to be available - // before it can get created. - // - // This means this needs to be done in the lambda for the setInitializer(...) call - // defined above as well as in fromMap! } CMakeBuildConfiguration::~CMakeBuildConfiguration() @@ -181,16 +198,11 @@ CMakeBuildConfiguration::~CMakeBuildConfiguration() QVariantMap CMakeBuildConfiguration::toMap() const { QVariantMap map(ProjectExplorer::BuildConfiguration::toMap()); - const QStringList config - = Utils::transform(m_configurationForCMake, [](const CMakeConfigItem &i) { return i.toString(); }); - map.insert(QLatin1String(CONFIGURATION_KEY), config); return map; } bool CMakeBuildConfiguration::fromMap(const QVariantMap &map) { - QTC_CHECK(!m_buildSystem); - if (!BuildConfiguration::fromMap(map)) return false; @@ -199,19 +211,33 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map) [](const QString &v) { return CMakeConfigItem::fromString(v); }), [](const CMakeConfigItem &c) { return !c.isNull(); }); - setConfigurationForCMake(conf); - - m_buildSystem = new CMakeBuildSystem(this); + // TODO: Upgrade from Qt Creator < 4.13: Remove when no longer supported! + const QString buildTypeName = [this]() { + switch (buildType()) { + case Debug: + return QString("Debug"); + case Profile: + return QString("RelWithDebInfo"); + case Release: + return QString("Release"); + case Unknown: + default: + return QString(""); + } + }(); + if (initialCMakeArguments().isEmpty()) { + QStringList initialArgs = defaultInitialCMakeArguments(target()->kit(), + buildTypeName) + + Utils::transform(conf, [this](const CMakeConfigItem &i) { + return i.toArgument(macroExpander()); + }); + + setInitialCMakeArguments(initialArgs); + } return true; } - - - - - - FilePath CMakeBuildConfiguration::shadowBuildDirectory(const FilePath &projectFilePath, const Kit *k, const QString &bcName, @@ -236,16 +262,16 @@ void CMakeBuildConfiguration::buildTarget(const QString &buildTarget) return bs->id() == Constants::CMAKE_BUILD_STEP_ID; })); - QString originalBuildTarget; + QStringList originalBuildTargets; if (cmBs) { - originalBuildTarget = cmBs->buildTarget(); - cmBs->setBuildTarget(buildTarget); + originalBuildTargets = cmBs->buildTargets(); + cmBs->setBuildTargets({buildTarget}); } BuildManager::buildList(buildSteps()); if (cmBs) - cmBs->setBuildTarget(originalBuildTarget); + cmBs->setBuildTargets(originalBuildTargets); } CMakeConfig CMakeBuildConfiguration::configurationFromCMake() const @@ -253,57 +279,34 @@ CMakeConfig CMakeBuildConfiguration::configurationFromCMake() const return m_configurationFromCMake; } -void CMakeBuildConfiguration::setConfigurationFromCMake(const CMakeConfig &config) +QStringList CMakeBuildConfiguration::extraCMakeArguments() const { - m_configurationFromCMake = config; + return m_extraCMakeArguments; } -void CMakeBuildConfiguration::setConfigurationForCMake(const QList<ConfigModel::DataItem> &items) +QStringList CMakeBuildConfiguration::initialCMakeArguments() const { - const CMakeConfig newConfig = Utils::transform(items, [](const ConfigModel::DataItem &i) { - CMakeConfigItem ni; - ni.key = i.key.toUtf8(); - ni.value = i.value.toUtf8(); - ni.documentation = i.description.toUtf8(); - ni.isAdvanced = i.isAdvanced; - ni.isUnset = i.isUnset; - ni.inCMakeCache = i.inCMakeCache; - ni.values = i.values; - switch (i.type) { - case CMakeProjectManager::ConfigModel::DataItem::BOOLEAN: - ni.type = CMakeConfigItem::BOOL; - break; - case CMakeProjectManager::ConfigModel::DataItem::FILE: - ni.type = CMakeConfigItem::FILEPATH; - break; - case CMakeProjectManager::ConfigModel::DataItem::DIRECTORY: - ni.type = CMakeConfigItem::PATH; - break; - case CMakeProjectManager::ConfigModel::DataItem::STRING: - ni.type = CMakeConfigItem::STRING; - break; - case CMakeProjectManager::ConfigModel::DataItem::UNKNOWN: - default: - ni.type = CMakeConfigItem::INTERNAL; - break; - } - return ni; - }); + return aspect<InitialCMakeArgumentsAspect>()->value().split('\n', Utils::SkipEmptyParts); +} - const CMakeConfig config = configurationForCMake() + newConfig; - setConfigurationForCMake(config); - - if (Utils::indexOf(newConfig, [](const CMakeConfigItem &item){ - return item.key.startsWith("ANDROID_BUILD_ABI_"); - }) != -1) { - // We always need to clean when we change the ANDROID_BUILD_ABI_ variables - QList<ProjectExplorer::BuildStepList *> stepLists; - const Core::Id clean = ProjectExplorer::Constants::BUILDSTEPS_CLEAN; - stepLists << cleanSteps(); - BuildManager::buildLists(stepLists, QStringList() << ProjectExplorerPlugin::displayNameForStepId(clean)); - } +void CMakeBuildConfiguration::setExtraCMakeArguments(const QStringList &args) +{ + if (m_extraCMakeArguments == args) + return; + + qCDebug(cmakeBuildConfigurationLog) + << "Extra Args changed from" << m_extraCMakeArguments << "to" << args << "..."; + m_extraCMakeArguments = args; +} + +void CMakeBuildConfiguration::setConfigurationFromCMake(const CMakeConfig &config) +{ + m_configurationFromCMake = config; } +// FIXME: Run clean steps when a setting starting with "ANDROID_BUILD_ABI_" is changed. +// FIXME: Warn when kit settings are overridden by a project. + void CMakeBuildConfiguration::clearError(ForceEnabledChanged fec) { if (!m_error.isEmpty()) { @@ -316,53 +319,9 @@ void CMakeBuildConfiguration::clearError(ForceEnabledChanged fec) } } -static CMakeConfig removeDuplicates(const CMakeConfig &config) -{ - CMakeConfig result; - // Remove duplicates (last value wins): - QSet<QByteArray> knownKeys; - for (int i = config.count() - 1; i >= 0; --i) { - const CMakeConfigItem &item = config.at(i); - if (knownKeys.contains(item.key)) - continue; - result.append(item); - knownKeys.insert(item.key); - } - Utils::sort(result, CMakeConfigItem::sortOperator()); - return result; -} - -void CMakeBuildConfiguration::setConfigurationForCMake(const CMakeConfig &config) -{ - auto configs = removeDuplicates(config); - if (m_configurationForCMake.isEmpty()) - m_configurationForCMake = removeDuplicates(m_initialConfiguration + - CMakeConfigurationKitAspect::configuration(target()->kit()) + configs); - else - m_configurationForCMake = configs; - - const Kit *k = target()->kit(); - CMakeConfig kitConfig = CMakeConfigurationKitAspect::configuration(k); - bool hasKitOverride = false; - foreach (const CMakeConfigItem &i, m_configurationForCMake) { - const QString b = CMakeConfigItem::expandedValueOf(k, i.key, kitConfig); - if (!b.isNull() && (i.expandedValue(k) != b || i.isUnset)) { - hasKitOverride = true; - break; - } - } - - if (hasKitOverride) - setWarning(tr("CMake configuration set by the kit was overridden in the project.")); - else - setWarning(QString()); - - emit configurationForCMakeChanged(); -} - -CMakeConfig CMakeBuildConfiguration::configurationForCMake() const +void CMakeBuildConfiguration::setInitialCMakeArguments(const QStringList &args) { - return removeDuplicates(CMakeConfigurationKitAspect::configuration(target()->kit()) + m_configurationForCMake); + aspect<InitialCMakeArgumentsAspect>()->setValue(args.join('\n')); } void CMakeBuildConfiguration::setError(const QString &message) @@ -377,7 +336,7 @@ void CMakeBuildConfiguration::setError(const QString &message) qCDebug(cmakeBuildConfigurationLog) << "Emitting enabledChanged signal"; emit enabledChanged(); } - emit errorOccured(m_error); + emit errorOccurred(m_error); } void CMakeBuildConfiguration::setWarning(const QString &message) @@ -385,15 +344,9 @@ void CMakeBuildConfiguration::setWarning(const QString &message) if (m_warning == message) return; m_warning = message; - emit warningOccured(m_warning); + emit warningOccurred(m_warning); } -bool CMakeBuildConfiguration::isQmlDebuggingEnabled() const -{ - return aspect<QtSupport::QmlDebuggingAspect>()->setting() == TriState::Enabled; -} - - QString CMakeBuildConfiguration::error() const { return m_error; @@ -418,8 +371,8 @@ CMakeBuildConfigurationFactory::CMakeBuildConfigurationFactory() registerBuildConfiguration<CMakeBuildConfiguration>( "CMakeProjectManager.CMakeBuildConfiguration"); - setSupportedProjectType(CMakeProjectManager::Constants::CMAKEPROJECT_ID); - setSupportedProjectMimeTypeName(Constants::CMAKEPROJECTMIMETYPE); + setSupportedProjectType(CMakeProjectManager::Constants::CMAKE_PROJECT_ID); + setSupportedProjectMimeTypeName(Constants::CMAKE_PROJECT_MIMETYPE); setBuildGenerator([](const Kit *k, const FilePath &projectPath, bool forSetup) { QList<BuildInfo> result; @@ -509,19 +462,7 @@ BuildInfo CMakeBuildConfigurationFactory::createBuildInfo(BuildType buildType) ProjectExplorer::BuildConfiguration::BuildType CMakeBuildConfiguration::buildType() const { - QByteArray cmakeBuildTypeName; - QFile cmakeCache(buildDirectory().toString() + QLatin1String("/CMakeCache.txt")); - if (cmakeCache.open(QIODevice::ReadOnly)) { - while (!cmakeCache.atEnd()) { - QByteArray line = cmakeCache.readLine(); - if (line.startsWith("CMAKE_BUILD_TYPE")) { - if (int pos = line.indexOf('=')) - cmakeBuildTypeName = line.mid(pos + 1).trimmed(); - break; - } - } - cmakeCache.close(); - } + QByteArray cmakeBuildTypeName = CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", m_configurationFromCMake); // Cover all common CMake build types const CMakeBuildConfigurationFactory::BuildType cmakeBuildType @@ -534,5 +475,21 @@ BuildSystem *CMakeBuildConfiguration::buildSystem() const return m_buildSystem; } +void CMakeBuildConfiguration::runCMakeWithExtraArguments() +{ + m_buildSystem->runCMakeWithExtraArguments(); +} + +// ---------------------------------------------------------------------- +// - InitialCMakeParametersAspect: +// ---------------------------------------------------------------------- + +InitialCMakeArgumentsAspect::InitialCMakeArgumentsAspect() +{ + setSettingsKey("CMake.Initial.Parameters"); + setLabelText(tr("Initial CMake parameters:")); + setDisplayStyle(TextEditDisplay); +} + } // namespace Internal } // namespace CMakeProjectManager |