From b25ad292d3124f4ec2b369ae0eb111f5e0c442f4 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 16 Nov 2018 14:19:39 +0100 Subject: [PATCH] ProjectExplorer: merge AbstractMsvcToolChain into MsvcToolChain The abstraction was initially done for the WinCE tool chain. Since we do not support WinCE anymore merge this classes back together. This reduces the maintenance burden, beacause there was no clear line on what functionality belongs in which abstraction layer. Change-Id: I70b75482f83538221789369acea5b8df6d89af75 Reviewed-by: Tobias Hunger --- .../projectexplorer/abstractmsvctoolchain.cpp | 481 ------------------ .../projectexplorer/abstractmsvctoolchain.h | 118 ----- src/plugins/projectexplorer/msvctoolchain.cpp | 419 ++++++++++++++- src/plugins/projectexplorer/msvctoolchain.h | 72 ++- .../projectexplorer/projectexplorer.pro | 2 - .../projectexplorer/projectexplorer.qbs | 2 - tests/auto/debugger/tst_dumpers.cpp | 2 +- 7 files changed, 477 insertions(+), 619 deletions(-) delete mode 100644 src/plugins/projectexplorer/abstractmsvctoolchain.cpp delete mode 100644 src/plugins/projectexplorer/abstractmsvctoolchain.h diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp deleted file mode 100644 index 33861c36815..00000000000 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp +++ /dev/null @@ -1,481 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "abstractmsvctoolchain.h" - -#include "msvcparser.h" -#include "projectexplorer.h" -#include "projectexplorersettings.h" -#include "taskhub.h" -#include "toolchaincache.h" - -#include -#include -#include -#include - -#include -#include - -enum { debug = 0 }; - -namespace ProjectExplorer { -namespace Internal { - -AbstractMsvcToolChain::AbstractMsvcToolChain(Core::Id typeId, Core::Id l, Detection d, - const Abi &abi, - const QString& vcvarsBat) : ToolChain(typeId, d), - m_predefinedMacrosCache(std::make_shared>()), - m_lastEnvironment(Utils::Environment::systemEnvironment()), - m_headerPathsMutex(new QMutex), - m_abi(abi), - m_vcvarsBat(vcvarsBat) -{ - setLanguage(l); -} - -AbstractMsvcToolChain::AbstractMsvcToolChain(Core::Id typeId, Detection d) : - ToolChain(typeId, d), - m_predefinedMacrosCache(std::make_shared>()), - m_lastEnvironment(Utils::Environment::systemEnvironment()) -{ } - -AbstractMsvcToolChain::~AbstractMsvcToolChain() -{ - delete m_headerPathsMutex; -} - -AbstractMsvcToolChain::AbstractMsvcToolChain(const AbstractMsvcToolChain &other) - : ToolChain(other), - m_debuggerCommand(other.m_debuggerCommand), - m_lastEnvironment(other.m_lastEnvironment), - m_resultEnvironment(other.m_resultEnvironment), - m_headerPathsMutex(new QMutex), - m_abi(other.m_abi), - m_vcvarsBat(other.m_vcvarsBat) -{ -} - -Abi AbstractMsvcToolChain::targetAbi() const -{ - return m_abi; -} - -bool AbstractMsvcToolChain::isValid() const -{ - if (m_vcvarsBat.isEmpty()) - return false; - QFileInfo fi(m_vcvarsBat); - return fi.isFile() && fi.isExecutable(); -} - -QString AbstractMsvcToolChain::originalTargetTriple() const -{ - return m_abi.wordWidth() == 64 - ? QLatin1String("x86_64-pc-windows-msvc") - : QLatin1String("i686-pc-windows-msvc"); -} - -bool static hasFlagEffectOnMacros(const QString &flag) -{ - if (flag.startsWith("-") || flag.startsWith("/")) { - const QString f = flag.mid(1); - if (f.startsWith("I")) - return false; // Skip include paths - if (f.startsWith("w", Qt::CaseInsensitive)) - return false; // Skip warning options - } - return true; -} - -ToolChain::MacroInspectionRunner AbstractMsvcToolChain::createMacroInspectionRunner() const -{ - Utils::Environment env(m_lastEnvironment); - addToEnvironment(env); - std::shared_ptr> macroCache = m_predefinedMacrosCache; - const Core::Id lang = language(); - - // This runner must be thread-safe! - return [this, env, macroCache, lang](const QStringList &cxxflags) { - const QStringList filteredFlags = Utils::filtered(cxxflags, [](const QString &arg) { - return hasFlagEffectOnMacros(arg); - }); - - const Utils::optional cachedMacros = macroCache->check(filteredFlags); - if (cachedMacros) - return cachedMacros.value(); - - const Macros macros = msvcPredefinedMacros(filteredFlags, env); - - const auto report = MacroInspectionReport{macros, msvcLanguageVersion(lang, macros)}; - macroCache->insert(filteredFlags, report); - - return report; - }; -} - -ProjectExplorer::Macros AbstractMsvcToolChain::predefinedMacros(const QStringList &cxxflags) const -{ - return createMacroInspectionRunner()(cxxflags).macros; -} - -LanguageExtensions AbstractMsvcToolChain::languageExtensions(const QStringList &cxxflags) const -{ - LanguageExtensions extensions(LanguageExtension::Microsoft); - if (cxxflags.contains(QLatin1String("/openmp"))) - extensions |= LanguageExtension::OpenMP; - - // see http://msdn.microsoft.com/en-us/library/0k0w269d%28v=vs.71%29.aspx - if (cxxflags.contains(QLatin1String("/Za"))) - extensions &= ~LanguageExtensions(LanguageExtension::Microsoft); - - return extensions; -} - -/** - * Converts MSVC warning flags to clang flags. - * @see http://msdn.microsoft.com/en-us/library/thxezb7y.aspx - */ -WarningFlags AbstractMsvcToolChain::warningFlags(const QStringList &cflags) const -{ - WarningFlags flags = WarningFlags::NoWarnings; - foreach (QString flag, cflags) { - if (!flag.isEmpty() && flag[0] == QLatin1Char('-')) - flag[0] = QLatin1Char('/'); - - if (flag == QLatin1String("/WX")) - flags |= WarningFlags::AsErrors; - else if (flag == QLatin1String("/W0") || flag == QLatin1String("/w")) - inferWarningsForLevel(0, flags); - else if (flag == QLatin1String("/W1")) - inferWarningsForLevel(1, flags); - else if (flag == QLatin1String("/W2")) - inferWarningsForLevel(2, flags); - else if (flag == QLatin1String("/W3") || flag == QLatin1String("/W4") || flag == QLatin1String("/Wall")) - inferWarningsForLevel(3, flags); - - WarningFlagAdder add(flag, flags); - if (add.triggered()) - continue; - // http://msdn.microsoft.com/en-us/library/ay4h0tc9.aspx - add(4263, WarningFlags::OverloadedVirtual); - // http://msdn.microsoft.com/en-us/library/ytxde1x7.aspx - add(4230, WarningFlags::IgnoredQualfiers); - // not exact match, http://msdn.microsoft.com/en-us/library/0hx5ckb0.aspx - add(4258, WarningFlags::HiddenLocals); - // http://msdn.microsoft.com/en-us/library/wzxffy8c.aspx - add(4265, WarningFlags::NonVirtualDestructor); - // http://msdn.microsoft.com/en-us/library/y92ktdf2%28v=vs.90%29.aspx - add(4018, WarningFlags::SignedComparison); - // http://msdn.microsoft.com/en-us/library/w099eeey%28v=vs.90%29.aspx - add(4068, WarningFlags::UnknownPragma); - // http://msdn.microsoft.com/en-us/library/26kb9fy0%28v=vs.80%29.aspx - add(4100, WarningFlags::UnusedParams); - // http://msdn.microsoft.com/en-us/library/c733d5h9%28v=vs.90%29.aspx - add(4101, WarningFlags::UnusedLocals); - // http://msdn.microsoft.com/en-us/library/xb1db44s%28v=vs.90%29.aspx - add(4189, WarningFlags::UnusedLocals); - // http://msdn.microsoft.com/en-us/library/ttcz0bys%28v=vs.90%29.aspx - add(4996, WarningFlags::Deprecated); - } - return flags; -} - -ToolChain::BuiltInHeaderPathsRunner AbstractMsvcToolChain::createBuiltInHeaderPathsRunner() const -{ - Utils::Environment env(m_lastEnvironment); - addToEnvironment(env); - - return [this, env](const QStringList &, const QString &) { - QMutexLocker locker(m_headerPathsMutex); - if (m_headerPaths.isEmpty()) { - foreach (const QString &path, env.value(QLatin1String("INCLUDE")).split(QLatin1Char(';'))) - m_headerPaths.append({path, HeaderPathType::BuiltIn}); - } - return m_headerPaths; - }; -} - -HeaderPaths AbstractMsvcToolChain::builtInHeaderPaths(const QStringList &cxxflags, - const Utils::FileName &sysRoot) const -{ - return createBuiltInHeaderPathsRunner()(cxxflags, sysRoot.toString()); -} - -void AbstractMsvcToolChain::addToEnvironment(Utils::Environment &env) const -{ - // We cache the full environment (incoming + modifications by setup script). - if (!m_resultEnvironment.size() || env != m_lastEnvironment) { - if (debug) - qDebug() << "addToEnvironment: " << displayName(); - m_lastEnvironment = env; - m_resultEnvironment = readEnvironmentSetting(env); - } - env = m_resultEnvironment; -} - -static QString wrappedMakeCommand(const QString &command) -{ - const QString wrapperPath = QDir::currentPath() + "/msvc_make.bat"; - QFile wrapper(wrapperPath); - if (!wrapper.open(QIODevice::WriteOnly)) - return command; - QTextStream stream(&wrapper); - stream << "chcp 65001\n"; - stream << command << " %*"; - - return wrapperPath; -} - -QString AbstractMsvcToolChain::makeCommand(const Utils::Environment &environment) const -{ - bool useJom = ProjectExplorerPlugin::projectExplorerSettings().useJom; - const QString jom("jom.exe"); - const QString nmake("nmake.exe"); - Utils::FileName tmp; - - QString command; - if (useJom) { - tmp = environment.searchInPath(jom, {Utils::FileName::fromString(QCoreApplication::applicationDirPath())}); - if (!tmp.isEmpty()) - command = tmp.toString(); - } - - if (command.isEmpty()) { - tmp = environment.searchInPath(nmake); - if (!tmp.isEmpty()) - command = tmp.toString(); - } - - if (command.isEmpty()) - command = useJom ? jom : nmake; - - if (environment.hasKey("VSLANG")) - return wrappedMakeCommand(command); - - return command; -} - -Utils::FileName AbstractMsvcToolChain::compilerCommand() const -{ - Utils::Environment env = Utils::Environment::systemEnvironment(); - addToEnvironment(env); - - Utils::FileName clexe = env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FileName &name) { - QDir dir(QDir::cleanPath(name.toFileInfo().absolutePath() + QStringLiteral("/.."))); - do { - if (QFile::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat"))) - || QFile::exists(dir.absolutePath() + "/Auxiliary/Build/vcvarsall.bat")) - return true; - } while (dir.cdUp() && !dir.isRoot()); - return false; - }); - return clexe; -} - -IOutputParser *AbstractMsvcToolChain::outputParser() const -{ - return new MsvcParser; -} - -bool AbstractMsvcToolChain::canClone() const -{ - return true; -} - -Utils::optional AbstractMsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env, - const QString &batchFile, - const QString &batchArgs, - QMap &envPairs) -{ - const QString marker = "####################"; - // Create a temporary file name for the output. Use a temporary file here - // as I don't know another way to do this in Qt... - - // Create a batch file to create and save the env settings - Utils::TempFileSaver saver(Utils::TemporaryDirectory::masterDirectoryPath() + "/XXXXXX.bat"); - - QByteArray call = "call "; - call += Utils::QtcProcess::quoteArg(batchFile).toLocal8Bit(); - if (!batchArgs.isEmpty()) { - call += ' '; - call += batchArgs.toLocal8Bit(); - } - if (Utils::HostOsInfo::isWindowsHost()) - saver.write("chcp 65001\r\n"); - saver.write(call + "\r\n"); - saver.write("@echo " + marker.toLocal8Bit() + "\r\n"); - saver.write("set\r\n"); - saver.write("@echo " + marker.toLocal8Bit() + "\r\n"); - if (!saver.finalize()) { - qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString())); - return QString(); - } - - Utils::SynchronousProcess run; - - // As of WinSDK 7.1, there is logic preventing the path from being set - // correctly if "ORIGINALPATH" is already set. That can cause problems - // if Creator is launched within a session set up by setenv.cmd. - Utils::Environment runEnv = env; - runEnv.unset(QLatin1String("ORIGINALPATH")); - run.setEnvironment(runEnv.toStringList()); - run.setTimeoutS(30); - Utils::FileName cmdPath = Utils::FileName::fromUserInput(QString::fromLocal8Bit(qgetenv("COMSPEC"))); - if (cmdPath.isEmpty()) - cmdPath = env.searchInPath(QLatin1String("cmd.exe")); - // Windows SDK setup scripts require command line switches for environment expansion. - QStringList cmdArguments({ - QLatin1String("/E:ON"), QLatin1String("/V:ON"), QLatin1String("/c")}); - cmdArguments << QDir::toNativeSeparators(saver.fileName()); - if (debug) - qDebug() << "readEnvironmentSetting: " << call << cmdPath << cmdArguments.join(' ') - << " Env: " << runEnv.size(); - run.setCodec(QTextCodec::codecForName("UTF-8")); - Utils::SynchronousProcessResponse response = run.runBlocking(cmdPath.toString(), cmdArguments); - - if (response.result != Utils::SynchronousProcessResponse::Finished) { - const QString message = !response.stdErr().isEmpty() - ? response.stdErr() - : response.exitMessage(cmdPath.toString(), 10); - qWarning().noquote() << message; - QString command = QDir::toNativeSeparators(batchFile); - if (!batchArgs.isEmpty()) - command += ' ' + batchArgs; - return QCoreApplication::translate("ProjectExplorer::Internal::AbstractMsvcToolChain", - "Failed to retrieve MSVC Environment from \"%1\":\n" - "%2").arg(command, message); - } - - // The SDK/MSVC scripts do not return exit codes != 0. Check on stdout. - const QString stdOut = response.stdOut(); - - // - // Now parse the file to get the environment settings - const int start = stdOut.indexOf(marker); - if (start == -1) { - qWarning("Could not find start marker in stdout output."); - return QString(); - } - - const int end = stdOut.indexOf(marker, start + 1); - if (end == -1) { - qWarning("Could not find end marker in stdout output."); - return QString(); - } - - const QString output = stdOut.mid(start, end - start); - - foreach (const QString &line, output.split(QLatin1String("\n"))) { - const int pos = line.indexOf('='); - if (pos > 0) { - const QString varName = line.mid(0, pos); - const QString varValue = line.mid(pos + 1); - envPairs.insert(varName, varValue); - } - } - - return Utils::nullopt; -} - -/** - * Infers warnings settings on warning level set - * @see http://msdn.microsoft.com/en-us/library/23k5d385.aspx - */ -void AbstractMsvcToolChain::inferWarningsForLevel(int warningLevel, WarningFlags &flags) -{ - // reset all except unrelated flag - flags = flags & WarningFlags::AsErrors; - - if (warningLevel >= 1) - flags |= WarningFlags(WarningFlags::Default | WarningFlags::IgnoredQualfiers | WarningFlags::HiddenLocals | WarningFlags::UnknownPragma); - if (warningLevel >= 2) - flags |= WarningFlags::All; - if (warningLevel >= 3) { - flags |= WarningFlags(WarningFlags::Extra | WarningFlags::NonVirtualDestructor | WarningFlags::SignedComparison - | WarningFlags::UnusedLocals | WarningFlags::Deprecated); - } - if (warningLevel >= 4) - flags |= WarningFlags::UnusedParams; -} - -void Internal::AbstractMsvcToolChain::toolChainUpdated() -{ - m_predefinedMacrosCache->invalidate(); -} - -bool AbstractMsvcToolChain::operator ==(const ToolChain &other) const -{ - if (!ToolChain::operator ==(other)) - return false; - - const auto *msvcTc = static_cast(&other); - return targetAbi() == msvcTc->targetAbi() - && m_vcvarsBat == msvcTc->m_vcvarsBat; -} - -AbstractMsvcToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, - WarningFlags &flags) : - m_flags(flags) -{ - if (flag.startsWith(QLatin1String("-wd"))) { - m_doesEnable = false; - } else if (flag.startsWith(QLatin1String("-w"))) { - m_doesEnable = true; - } else { - m_triggered = true; - return; - } - bool ok = false; - if (m_doesEnable) - m_warningCode = flag.midRef(2).toInt(&ok); - else - m_warningCode = flag.midRef(3).toInt(&ok); - if (!ok) - m_triggered = true; -} - -void AbstractMsvcToolChain::WarningFlagAdder::operator ()(int warningCode, WarningFlags flagsSet) -{ - if (m_triggered) - return; - if (warningCode == m_warningCode) - { - m_triggered = true; - if (m_doesEnable) - m_flags |= flagsSet; - else - m_flags &= ~flagsSet; - } -} - -bool AbstractMsvcToolChain::WarningFlagAdder::triggered() const -{ - return m_triggered; -} - -} // namespace Internal -} // namespace ProjectExplorer - diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.h b/src/plugins/projectexplorer/abstractmsvctoolchain.h deleted file mode 100644 index 8fc7c46be50..00000000000 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.h +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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. -** -****************************************************************************/ - -#pragma once - -#include "toolchain.h" -#include "toolchaincache.h" -#include "abi.h" -#include "headerpath.h" - -#include - -#include -#include -#include - -namespace ProjectExplorer { -namespace Internal { - -class PROJECTEXPLORER_EXPORT AbstractMsvcToolChain : public ToolChain -{ -public: - explicit AbstractMsvcToolChain(Core::Id typeId, Core::Id l, Detection d, - const Abi &abi, const QString& vcvarsBat); - explicit AbstractMsvcToolChain(Core::Id typeId, Detection d); - AbstractMsvcToolChain(const AbstractMsvcToolChain &other); - ~AbstractMsvcToolChain(); - - Abi targetAbi() const override; - - bool isValid() const override; - - QString originalTargetTriple() const override; - - MacroInspectionRunner createMacroInspectionRunner() const override; - Macros predefinedMacros(const QStringList &cxxflags) const override; - LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; - WarningFlags warningFlags(const QStringList &cflags) const override; - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override; - HeaderPaths builtInHeaderPaths(const QStringList &cxxflags, - const Utils::FileName &sysRoot) const override; - void addToEnvironment(Utils::Environment &env) const override; - - QString makeCommand(const Utils::Environment &environment) const override; - Utils::FileName compilerCommand() const override; - IOutputParser *outputParser() const override; - - bool canClone() const override; - - QString varsBat() const { return m_vcvarsBat; } - - bool operator ==(const ToolChain &) const override; - - static Utils::optional generateEnvironmentSettings(const Utils::Environment &env, - const QString &batchFile, - const QString &batchArgs, - QMap &envPairs); - -protected: - class WarningFlagAdder - { - int m_warningCode = 0; - WarningFlags &m_flags; - bool m_doesEnable = false; - bool m_triggered = false; - public: - WarningFlagAdder(const QString &flag, WarningFlags &flags); - void operator ()(int warningCode, WarningFlags flagsSet); - - bool triggered() const; - }; - - static void inferWarningsForLevel(int warningLevel, WarningFlags &flags); - void toolChainUpdated() override; - virtual Utils::Environment readEnvironmentSetting(const Utils::Environment& env) const = 0; - // Function must be thread-safe! - virtual Macros msvcPredefinedMacros(const QStringList cxxflags, - const Utils::Environment& env) const = 0; - virtual LanguageVersion msvcLanguageVersion(const Core::Id &language, - const Macros ¯os) const = 0; - - Utils::FileName m_debuggerCommand; - - mutable std::shared_ptr> m_predefinedMacrosCache; - - mutable Utils::Environment m_lastEnvironment; // Last checked 'incoming' environment. - mutable Utils::Environment m_resultEnvironment; // Resulting environment for VC - mutable QMutex *m_headerPathsMutex = nullptr; - mutable HeaderPaths m_headerPaths; - Abi m_abi; - - QString m_vcvarsBat; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 7638884aad6..4847d8863e0 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -29,6 +29,7 @@ #include "msvcparser.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" +#include "projectexplorersettings.h" #include "taskhub.h" #include "toolchainmanager.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -674,8 +676,14 @@ MsvcToolChain::MsvcToolChain(const QString &name, const Abi &abi, { } MsvcToolChain::MsvcToolChain(const MsvcToolChain &other) - : AbstractMsvcToolChain(other.typeId(), other.language(), other.detection(), other.targetAbi(), other.varsBat()) + : ToolChain(other) , m_environmentModifications(other.m_environmentModifications) + , m_debuggerCommand(other.m_debuggerCommand) + , m_lastEnvironment(other.m_lastEnvironment) + , m_resultEnvironment(other.m_resultEnvironment) + , m_headerPathsMutex(new QMutex) + , m_abi(other.m_abi) + , m_vcvarsBat(other.m_vcvarsBat) , m_varsBatArg(other.m_varsBatArg) { if (other.m_envModWatcher.isRunning()) { @@ -698,9 +706,15 @@ MsvcToolChain::MsvcToolChain(const MsvcToolChain &other) MsvcToolChain::MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi, const QString &varsBat, const QString &varsBatArg, Core::Id l, Detection d) - : AbstractMsvcToolChain(typeId, l, d, abi, varsBat) + : ToolChain(typeId, d) + , m_predefinedMacrosCache(std::make_shared>()) + , m_lastEnvironment(Utils::Environment::systemEnvironment()) + , m_headerPathsMutex(new QMutex) + , m_abi(abi) + , m_vcvarsBat(varsBat) , m_varsBatArg(varsBatArg) { + setLanguage(l); initEnvModWatcher(Utils::runAsync(envModThreadPool(), &MsvcToolChain::environmentModifications, varsBat, varsBatArg)); @@ -710,12 +724,62 @@ MsvcToolChain::MsvcToolChain(Core::Id typeId, const QString &name, const Abi &ab setDisplayName(name); } -MsvcToolChain::MsvcToolChain(Core::Id typeId) : AbstractMsvcToolChain(typeId, ManualDetection) +MsvcToolChain::MsvcToolChain(Core::Id typeId) + : ToolChain(typeId, ManualDetection) + , m_predefinedMacrosCache(std::make_shared>()) + , m_lastEnvironment(Utils::Environment::systemEnvironment()) { } +void MsvcToolChain::inferWarningsForLevel(int warningLevel, WarningFlags &flags) +{ + // reset all except unrelated flag + flags = flags & WarningFlags::AsErrors; + + if (warningLevel >= 1) + flags |= WarningFlags(WarningFlags::Default | WarningFlags::IgnoredQualfiers | WarningFlags::HiddenLocals | WarningFlags::UnknownPragma); + if (warningLevel >= 2) + flags |= WarningFlags::All; + if (warningLevel >= 3) { + flags |= WarningFlags(WarningFlags::Extra | WarningFlags::NonVirtualDestructor | WarningFlags::SignedComparison + | WarningFlags::UnusedLocals | WarningFlags::Deprecated); + } + if (warningLevel >= 4) + flags |= WarningFlags::UnusedParams; +} + +void MsvcToolChain::toolChainUpdated() +{ + m_predefinedMacrosCache->invalidate(); +} + MsvcToolChain::MsvcToolChain() : MsvcToolChain(Constants::MSVC_TOOLCHAIN_TYPEID) { } +MsvcToolChain::~MsvcToolChain() +{ + delete m_headerPathsMutex; +} + +Abi MsvcToolChain::targetAbi() const +{ + return m_abi; +} + +bool MsvcToolChain::isValid() const +{ + if (m_vcvarsBat.isEmpty()) + return false; + QFileInfo fi(m_vcvarsBat); + return fi.isFile() && fi.isExecutable(); +} + +QString MsvcToolChain::originalTargetTriple() const +{ + return m_abi.wordWidth() == 64 + ? QLatin1String("x86_64-pc-windows-msvc") + : QLatin1String("i686-pc-windows-msvc"); +} + QString MsvcToolChain::typeDisplayName() const { return MsvcToolChainFactory::tr("MSVC"); @@ -772,7 +836,7 @@ Utils::FileNameList MsvcToolChain::suggestedMkspecList() const QVariantMap MsvcToolChain::toMap() const { - QVariantMap data = AbstractMsvcToolChain::toMap(); + QVariantMap data = ToolChain::toMap(); data.insert(QLatin1String(varsBatKeyC), m_vcvarsBat); if (!m_varsBatArg.isEmpty()) data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg); @@ -785,7 +849,7 @@ QVariantMap MsvcToolChain::toMap() const bool MsvcToolChain::fromMap(const QVariantMap &data) { - if (!AbstractMsvcToolChain::fromMap(data)) + if (!ToolChain::fromMap(data)) return false; m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString()); m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString(); @@ -807,11 +871,213 @@ std::unique_ptr MsvcToolChain::createConfigurationWidget( return std::make_unique(this); } +bool MsvcToolChain::canClone() const +{ + return true; +} + ToolChain *MsvcToolChain::clone() const { return new MsvcToolChain(*this); } +bool static hasFlagEffectOnMacros(const QString &flag) +{ + if (flag.startsWith("-") || flag.startsWith("/")) { + const QString f = flag.mid(1); + if (f.startsWith("I")) + return false; // Skip include paths + if (f.startsWith("w", Qt::CaseInsensitive)) + return false; // Skip warning options + } + return true; +} + +ToolChain::MacroInspectionRunner MsvcToolChain::createMacroInspectionRunner() const +{ + Utils::Environment env(m_lastEnvironment); + addToEnvironment(env); + std::shared_ptr> macroCache = m_predefinedMacrosCache; + const Core::Id lang = language(); + + // This runner must be thread-safe! + return [this, env, macroCache, lang](const QStringList &cxxflags) { + const QStringList filteredFlags = Utils::filtered(cxxflags, [](const QString &arg) { + return hasFlagEffectOnMacros(arg); + }); + + const Utils::optional cachedMacros = macroCache->check(filteredFlags); + if (cachedMacros) + return cachedMacros.value(); + + const Macros macros = msvcPredefinedMacros(filteredFlags, env); + + const auto report = MacroInspectionReport{macros, languageVersion(lang, macros)}; + macroCache->insert(filteredFlags, report); + + return report; + }; +} + +Macros MsvcToolChain::predefinedMacros(const QStringList &cxxflags) const +{ + return createMacroInspectionRunner()(cxxflags).macros; +} + +LanguageExtensions MsvcToolChain::languageExtensions(const QStringList &cxxflags) const +{ + LanguageExtensions extensions(LanguageExtension::Microsoft); + if (cxxflags.contains(QLatin1String("/openmp"))) + extensions |= LanguageExtension::OpenMP; + + // see http://msdn.microsoft.com/en-us/library/0k0w269d%28v=vs.71%29.aspx + if (cxxflags.contains(QLatin1String("/Za"))) + extensions &= ~LanguageExtensions(LanguageExtension::Microsoft); + + return extensions; +} + +WarningFlags MsvcToolChain::warningFlags(const QStringList &cflags) const +{ + WarningFlags flags = WarningFlags::NoWarnings; + foreach (QString flag, cflags) { + if (!flag.isEmpty() && flag[0] == QLatin1Char('-')) + flag[0] = QLatin1Char('/'); + + if (flag == QLatin1String("/WX")) + flags |= WarningFlags::AsErrors; + else if (flag == QLatin1String("/W0") || flag == QLatin1String("/w")) + inferWarningsForLevel(0, flags); + else if (flag == QLatin1String("/W1")) + inferWarningsForLevel(1, flags); + else if (flag == QLatin1String("/W2")) + inferWarningsForLevel(2, flags); + else if (flag == QLatin1String("/W3") || flag == QLatin1String("/W4") || flag == QLatin1String("/Wall")) + inferWarningsForLevel(3, flags); + + WarningFlagAdder add(flag, flags); + if (add.triggered()) + continue; + // http://msdn.microsoft.com/en-us/library/ay4h0tc9.aspx + add(4263, WarningFlags::OverloadedVirtual); + // http://msdn.microsoft.com/en-us/library/ytxde1x7.aspx + add(4230, WarningFlags::IgnoredQualfiers); + // not exact match, http://msdn.microsoft.com/en-us/library/0hx5ckb0.aspx + add(4258, WarningFlags::HiddenLocals); + // http://msdn.microsoft.com/en-us/library/wzxffy8c.aspx + add(4265, WarningFlags::NonVirtualDestructor); + // http://msdn.microsoft.com/en-us/library/y92ktdf2%28v=vs.90%29.aspx + add(4018, WarningFlags::SignedComparison); + // http://msdn.microsoft.com/en-us/library/w099eeey%28v=vs.90%29.aspx + add(4068, WarningFlags::UnknownPragma); + // http://msdn.microsoft.com/en-us/library/26kb9fy0%28v=vs.80%29.aspx + add(4100, WarningFlags::UnusedParams); + // http://msdn.microsoft.com/en-us/library/c733d5h9%28v=vs.90%29.aspx + add(4101, WarningFlags::UnusedLocals); + // http://msdn.microsoft.com/en-us/library/xb1db44s%28v=vs.90%29.aspx + add(4189, WarningFlags::UnusedLocals); + // http://msdn.microsoft.com/en-us/library/ttcz0bys%28v=vs.90%29.aspx + add(4996, WarningFlags::Deprecated); + } + return flags; +} + +ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunner() const +{ + Utils::Environment env(m_lastEnvironment); + addToEnvironment(env); + + return [this, env](const QStringList &, const QString &) { + QMutexLocker locker(m_headerPathsMutex); + if (m_headerPaths.isEmpty()) { + foreach (const QString &path, env.value(QLatin1String("INCLUDE")).split(QLatin1Char(';'))) + m_headerPaths.append({path, HeaderPathType::BuiltIn}); + } + return m_headerPaths; + }; +} + +HeaderPaths MsvcToolChain::builtInHeaderPaths(const QStringList &cxxflags, const Utils::FileName &sysRoot) const +{ + return createBuiltInHeaderPathsRunner()(cxxflags, sysRoot.toString()); +} + +void MsvcToolChain::addToEnvironment(Utils::Environment &env) const +{ + // We cache the full environment (incoming + modifications by setup script). + if (!m_resultEnvironment.size() || env != m_lastEnvironment) { + if (debug) + qDebug() << "addToEnvironment: " << displayName(); + m_lastEnvironment = env; + m_resultEnvironment = readEnvironmentSetting(env); + } + env = m_resultEnvironment; +} + +static QString wrappedMakeCommand(const QString &command) +{ + const QString wrapperPath = QDir::currentPath() + "/msvc_make.bat"; + QFile wrapper(wrapperPath); + if (!wrapper.open(QIODevice::WriteOnly)) + return command; + QTextStream stream(&wrapper); + stream << "chcp 65001\n"; + stream << command << " %*"; + + return wrapperPath; +} + +QString MsvcToolChain::makeCommand(const Utils::Environment &environment) const +{ + bool useJom = ProjectExplorerPlugin::projectExplorerSettings().useJom; + const QString jom("jom.exe"); + const QString nmake("nmake.exe"); + Utils::FileName tmp; + + QString command; + if (useJom) { + tmp = environment.searchInPath(jom, {Utils::FileName::fromString(QCoreApplication::applicationDirPath())}); + if (!tmp.isEmpty()) + command = tmp.toString(); + } + + if (command.isEmpty()) { + tmp = environment.searchInPath(nmake); + if (!tmp.isEmpty()) + command = tmp.toString(); + } + + if (command.isEmpty()) + command = useJom ? jom : nmake; + + if (environment.hasKey("VSLANG")) + return wrappedMakeCommand(command); + + return command; +} + +Utils::FileName MsvcToolChain::compilerCommand() const +{ + Utils::Environment env = Utils::Environment::systemEnvironment(); + addToEnvironment(env); + + Utils::FileName clexe = env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FileName &name) { + QDir dir(QDir::cleanPath(name.toFileInfo().absolutePath() + QStringLiteral("/.."))); + do { + if (QFile::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat"))) + || QFile::exists(dir.absolutePath() + "/Auxiliary/Build/vcvarsall.bat")) + return true; + } while (dir.cdUp() && !dir.isRoot()); + return false; + }); + return clexe; +} + +IOutputParser *MsvcToolChain::outputParser() const +{ + return new MsvcParser; +} + // -------------------------------------------------------------------------- // MsvcBasedToolChainConfigWidget: Creates a simple GUI without error label // to display name and varsBat. Derived classes should add the error label and @@ -1367,10 +1633,13 @@ ToolChain *ClangClToolChainFactory::create(Core::Id l) bool MsvcToolChain::operator ==(const ToolChain &other) const { - if (!AbstractMsvcToolChain::operator ==(other)) + if (!ToolChain::operator ==(other)) return false; - const auto *msvcTc = static_cast(&other); - return m_varsBatArg == msvcTc->m_varsBatArg; + + const auto *msvcTc = dynamic_cast(&other); + return targetAbi() == msvcTc->targetAbi() + && m_vcvarsBat == msvcTc->m_vcvarsBat + && m_varsBatArg == msvcTc->m_varsBatArg; } void MsvcToolChain::cancelMsvcToolChainDetection() @@ -1378,6 +1647,101 @@ void MsvcToolChain::cancelMsvcToolChainDetection() envModThreadPool()->clear(); } +Utils::optional MsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env, + const QString &batchFile, + const QString &batchArgs, + QMap &envPairs) +{ + const QString marker = "####################"; + // Create a temporary file name for the output. Use a temporary file here + // as I don't know another way to do this in Qt... + + // Create a batch file to create and save the env settings + Utils::TempFileSaver saver(Utils::TemporaryDirectory::masterDirectoryPath() + "/XXXXXX.bat"); + + QByteArray call = "call "; + call += Utils::QtcProcess::quoteArg(batchFile).toLocal8Bit(); + if (!batchArgs.isEmpty()) { + call += ' '; + call += batchArgs.toLocal8Bit(); + } + if (Utils::HostOsInfo::isWindowsHost()) + saver.write("chcp 65001\r\n"); + saver.write(call + "\r\n"); + saver.write("@echo " + marker.toLocal8Bit() + "\r\n"); + saver.write("set\r\n"); + saver.write("@echo " + marker.toLocal8Bit() + "\r\n"); + if (!saver.finalize()) { + qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString())); + return QString(); + } + + Utils::SynchronousProcess run; + + // As of WinSDK 7.1, there is logic preventing the path from being set + // correctly if "ORIGINALPATH" is already set. That can cause problems + // if Creator is launched within a session set up by setenv.cmd. + Utils::Environment runEnv = env; + runEnv.unset(QLatin1String("ORIGINALPATH")); + run.setEnvironment(runEnv.toStringList()); + run.setTimeoutS(30); + Utils::FileName cmdPath = Utils::FileName::fromUserInput(QString::fromLocal8Bit(qgetenv("COMSPEC"))); + if (cmdPath.isEmpty()) + cmdPath = env.searchInPath(QLatin1String("cmd.exe")); + // Windows SDK setup scripts require command line switches for environment expansion. + QStringList cmdArguments({ + QLatin1String("/E:ON"), QLatin1String("/V:ON"), QLatin1String("/c")}); + cmdArguments << QDir::toNativeSeparators(saver.fileName()); + if (debug) + qDebug() << "readEnvironmentSetting: " << call << cmdPath << cmdArguments.join(' ') + << " Env: " << runEnv.size(); + run.setCodec(QTextCodec::codecForName("UTF-8")); + Utils::SynchronousProcessResponse response = run.runBlocking(cmdPath.toString(), cmdArguments); + + if (response.result != Utils::SynchronousProcessResponse::Finished) { + const QString message = !response.stdErr().isEmpty() + ? response.stdErr() + : response.exitMessage(cmdPath.toString(), 10); + qWarning().noquote() << message; + QString command = QDir::toNativeSeparators(batchFile); + if (!batchArgs.isEmpty()) + command += ' ' + batchArgs; + return QCoreApplication::translate("ProjectExplorer::Internal::MsvcToolChain", + "Failed to retrieve MSVC Environment from \"%1\":\n" + "%2").arg(command, message); + } + + // The SDK/MSVC scripts do not return exit codes != 0. Check on stdout. + const QString stdOut = response.stdOut(); + + // + // Now parse the file to get the environment settings + const int start = stdOut.indexOf(marker); + if (start == -1) { + qWarning("Could not find start marker in stdout output."); + return QString(); + } + + const int end = stdOut.indexOf(marker, start + 1); + if (end == -1) { + qWarning("Could not find end marker in stdout output."); + return QString(); + } + + const QString output = stdOut.mid(start, end - start); + + foreach (const QString &line, output.split(QLatin1String("\n"))) { + const int pos = line.indexOf('='); + if (pos > 0) { + const QString varName = line.mid(0, pos); + const QString varValue = line.mid(pos + 1); + envPairs.insert(varName, varValue); + } + } + + return Utils::nullopt; +} + bool MsvcToolChainFactory::canRestore(const QVariantMap &data) { const Core::Id id = typeIdFromMap(data); @@ -1410,5 +1774,44 @@ ToolChain *ClangClToolChainFactory::restore(const QVariantMap &data) return readFromMap(data); } +MsvcToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags) + : m_flags(flags) +{ + if (flag.startsWith(QLatin1String("-wd"))) { + m_doesEnable = false; + } else if (flag.startsWith(QLatin1String("-w"))) { + m_doesEnable = true; + } else { + m_triggered = true; + return; + } + bool ok = false; + if (m_doesEnable) + m_warningCode = flag.midRef(2).toInt(&ok); + else + m_warningCode = flag.midRef(3).toInt(&ok); + if (!ok) + m_triggered = true; +} + +void MsvcToolChain::WarningFlagAdder::operator ()(int warningCode, WarningFlags flagsSet) +{ + if (m_triggered) + return; + if (warningCode == m_warningCode) + { + m_triggered = true; + if (m_doesEnable) + m_flags |= flagsSet; + else + m_flags &= ~flagsSet; + } +} + +bool MsvcToolChain::WarningFlagAdder::triggered() const +{ + return m_triggered; +} + } // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index c1482b95b3a..eff342481ac 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -25,12 +25,14 @@ #pragma once -#include "abstractmsvctoolchain.h" #include "abi.h" +#include "toolchain.h" +#include "toolchaincache.h" #include "toolchainconfigwidget.h" #include +#include #include QT_FORWARD_DECLARE_CLASS(QLabel) @@ -45,7 +47,7 @@ namespace Internal { // MsvcToolChain // -------------------------------------------------------------------------- -class MsvcToolChain : public AbstractMsvcToolChain +class MsvcToolChain : public ToolChain { public: enum Type { WindowsSDK, VS }; @@ -65,6 +67,13 @@ public: Core::Id l, Detection d = ManualDetection); MsvcToolChain(const MsvcToolChain &other); MsvcToolChain(); + ~MsvcToolChain(); + + Abi targetAbi() const override; + + bool isValid() const override; + + QString originalTargetTriple() const override; Utils::FileNameList suggestedMkspecList() const override; @@ -75,27 +84,63 @@ public: std::unique_ptr createConfigurationWidget() override; + bool canClone() const override; ToolChain *clone() const override; + MacroInspectionRunner createMacroInspectionRunner() const override; + Macros predefinedMacros(const QStringList &cxxflags) const override; + LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; + WarningFlags warningFlags(const QStringList &cflags) const override; + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner() const override; + HeaderPaths builtInHeaderPaths(const QStringList &cxxflags, + const Utils::FileName &sysRoot) const override; + void addToEnvironment(Utils::Environment &env) const override; + + QString makeCommand(const Utils::Environment &environment) const override; + Utils::FileName compilerCommand() const override; + IOutputParser *outputParser() const override; + QString varsBatArg() const { return m_varsBatArg; } + QString varsBat() const { return m_vcvarsBat; } void setVarsBatArg(const QString &varsBA) { m_varsBatArg = varsBA; } bool operator == (const ToolChain &) const override; static void cancelMsvcToolChainDetection(); + static Utils::optional generateEnvironmentSettings(const Utils::Environment &env, + const QString &batchFile, + const QString &batchArgs, + QMap &envPairs); + protected: + class WarningFlagAdder + { + int m_warningCode = 0; + WarningFlags &m_flags; + bool m_doesEnable = false; + bool m_triggered = false; + public: + WarningFlagAdder(const QString &flag, WarningFlags &flags); + void operator ()(int warningCode, WarningFlags flagsSet); + + bool triggered() const; + }; + explicit MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi, const QString &varsBat, const QString &varsBatArg, Core::Id l, Detection d); explicit MsvcToolChain(Core::Id typeId); - Utils::Environment readEnvironmentSetting(const Utils::Environment& env) const final; + static void inferWarningsForLevel(int warningLevel, WarningFlags &flags); + void toolChainUpdated() override; + + Utils::Environment readEnvironmentSetting(const Utils::Environment& env) const; // Function must be thread-safe! - Macros msvcPredefinedMacros(const QStringList cxxflags, - const Utils::Environment &env) const override; - LanguageVersion msvcLanguageVersion(const Core::Id &language, - const Macros ¯os) const override; + virtual Macros msvcPredefinedMacros(const QStringList cxxflags, + const Utils::Environment &env) const; + virtual LanguageVersion msvcLanguageVersion(const Core::Id &language, + const Macros ¯os) const; private: struct GenerateEnvResult @@ -111,6 +156,19 @@ private: mutable QList m_environmentModifications; mutable QFutureWatcher m_envModWatcher; + Utils::FileName m_debuggerCommand; + + mutable std::shared_ptr> m_predefinedMacrosCache; + + mutable Utils::Environment m_lastEnvironment; // Last checked 'incoming' environment. + mutable Utils::Environment m_resultEnvironment; // Resulting environment for VC + mutable QMutex *m_headerPathsMutex = nullptr; + mutable HeaderPaths m_headerPaths; + +protected: + Abi m_abi; + + QString m_vcvarsBat; QString m_varsBatArg; // Argument }; diff --git a/src/plugins/projectexplorer/projectexplorer.pro b/src/plugins/projectexplorer/projectexplorer.pro index de8dca3b72d..21f70f48e26 100644 --- a/src/plugins/projectexplorer/projectexplorer.pro +++ b/src/plugins/projectexplorer/projectexplorer.pro @@ -315,12 +315,10 @@ FORMS += processstep.ui \ WINSOURCES += \ windebuginterface.cpp \ msvctoolchain.cpp \ - abstractmsvctoolchain.cpp WINHEADERS += \ windebuginterface.h \ msvctoolchain.h \ - abstractmsvctoolchain.h win32|equals(TEST, 1) { SOURCES += $$WINSOURCES diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index e98f926e938..d25b52c7d42 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -235,8 +235,6 @@ Project { name: "WindowsToolChains" condition: qbs.targetOS.contains("windows") || qtc.testsEnabled files: [ - "abstractmsvctoolchain.cpp", - "abstractmsvctoolchain.h", "msvctoolchain.cpp", "msvctoolchain.h", "windebuginterface.cpp", diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 89d54ab1bba..af402f4e8a1 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -47,7 +47,7 @@ using namespace Internal; #ifdef Q_CC_MSVC -// Copied from abstractmsvctoolchain.cpp to avoid plugin dependency. +// Copied from msvctoolchain.cpp to avoid plugin dependency. static bool generateEnvironmentSettings(Utils::Environment &env, const QString &batchFile, const QString &batchArgs,