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,