forked from qt-creator/qt-creator
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 <tobias.hunger@qt.io>
This commit is contained in:
@@ -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 <utils/hostosinfo.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QTextCodec>
|
||||
|
||||
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<Cache<MacroInspectionReport, 64>>()),
|
||||
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<Cache<MacroInspectionReport, 64>>()),
|
||||
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<Cache<MacroInspectionReport, 64>> 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<MacroInspectionReport> 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<QString> AbstractMsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env,
|
||||
const QString &batchFile,
|
||||
const QString &batchArgs,
|
||||
QMap<QString, QString> &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<const AbstractMsvcToolChain *>(&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
|
||||
|
@@ -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 <QMutex>
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/optional.h>
|
||||
|
||||
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<QString> generateEnvironmentSettings(const Utils::Environment &env,
|
||||
const QString &batchFile,
|
||||
const QString &batchArgs,
|
||||
QMap<QString, QString> &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<Cache<MacroInspectionReport, 64>> 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
|
@@ -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 <utils/hostosinfo.h>
|
||||
#include <utils/optional.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
@@ -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<Cache<MacroInspectionReport, 64>>())
|
||||
, 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<Cache<MacroInspectionReport, 64>>())
|
||||
, 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<ToolChainConfigWidget> MsvcToolChain::createConfigurationWidget(
|
||||
return std::make_unique<MsvcToolChainConfigWidget>(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<Cache<MacroInspectionReport, 64>> 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<MacroInspectionReport> 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<const MsvcToolChain *>(&other);
|
||||
return m_varsBatArg == msvcTc->m_varsBatArg;
|
||||
|
||||
const auto *msvcTc = dynamic_cast<const MsvcToolChain *>(&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<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::Environment &env,
|
||||
const QString &batchFile,
|
||||
const QString &batchArgs,
|
||||
QMap<QString, QString> &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<ClangClToolChain>(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
|
||||
|
@@ -25,12 +25,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "abstractmsvctoolchain.h"
|
||||
#include "abi.h"
|
||||
#include "toolchain.h"
|
||||
#include "toolchaincache.h"
|
||||
#include "toolchainconfigwidget.h"
|
||||
|
||||
#include <QFutureWatcher>
|
||||
|
||||
#include <utils/environment.h>
|
||||
#include <utils/optional.h>
|
||||
|
||||
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<ToolChainConfigWidget> 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<QString> generateEnvironmentSettings(const Utils::Environment &env,
|
||||
const QString &batchFile,
|
||||
const QString &batchArgs,
|
||||
QMap<QString, QString> &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<Utils::EnvironmentItem> m_environmentModifications;
|
||||
mutable QFutureWatcher<GenerateEnvResult> m_envModWatcher;
|
||||
|
||||
Utils::FileName m_debuggerCommand;
|
||||
|
||||
mutable std::shared_ptr<Cache<MacroInspectionReport, 64>> 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
|
||||
};
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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",
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user