aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib
diff options
context:
space:
mode:
authorJake Petroules <jake.petroules@qt.io>2016-05-22 15:37:18 -0700
committerJoerg Bornemann <joerg.bornemann@qt.io>2016-06-27 13:13:52 +0000
commit129e7a8ab1edfb583157db6050ab3f1bd426279e (patch)
tree6f3634e0c1ce07501a1368a774ef62fe1106f065 /src/lib/corelib
parent2ea9e28a6963cae217923d77fd00f581306b1980 (diff)
Determine Visual Studio architecture & build environment automatically.
This moves one step further to making the setup-toolchains tool unnecessary and also makes the toolchainInstallPath of MSVC profiles consistent with what Qt Creator sets. Change-Id: I3eb11b456bf02bde8993ec0dac7e0f9950174a08 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'src/lib/corelib')
-rw-r--r--src/lib/corelib/corelib.pro2
-rw-r--r--src/lib/corelib/corelib.qbs6
-rw-r--r--src/lib/corelib/jsextensions/utilitiesextension.cpp45
-rw-r--r--src/lib/corelib/tools/msvcinfo.cpp206
-rw-r--r--src/lib/corelib/tools/msvcinfo.h98
-rw-r--r--src/lib/corelib/tools/tools.pri8
-rw-r--r--src/lib/corelib/tools/vsenvironmentdetector.cpp191
-rw-r--r--src/lib/corelib/tools/vsenvironmentdetector.h67
8 files changed, 619 insertions, 4 deletions
diff --git a/src/lib/corelib/corelib.pro b/src/lib/corelib/corelib.pro
index 0a6a994f6..c2926f3a1 100644
--- a/src/lib/corelib/corelib.pro
+++ b/src/lib/corelib/corelib.pro
@@ -21,7 +21,7 @@ include(logging/logging.pri)
include(parser/parser.pri)
include(tools/tools.pri)
-win32:LIBS += -lpsapi
+win32:LIBS += -lpsapi -lshell32
HEADERS += \
qbs.h
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index 3bf313f02..280ff9c12 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -21,7 +21,7 @@ QbsLibrary {
Properties {
condition: qbs.targetOS.contains("windows")
- cpp.dynamicLibraries: base.concat(["Psapi"])
+ cpp.dynamicLibraries: base.concat(["Psapi", "shell32"])
}
cpp.dynamicLibraries: base
@@ -339,6 +339,8 @@ QbsLibrary {
"id.cpp",
"id.h",
"installoptions.cpp",
+ "msvcinfo.cpp",
+ "msvcinfo.h",
"persistence.cpp",
"persistence.h",
"persistentobject.h",
@@ -373,6 +375,8 @@ QbsLibrary {
"version.h",
"visualstudioversioninfo.cpp",
"visualstudioversioninfo.h",
+ "vsenvironmentdetector.cpp",
+ "vsenvironmentdetector.h",
"weakpointer.h"
]
}
diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp
index fb60da7fb..6e71c2946 100644
--- a/src/lib/corelib/jsextensions/utilitiesextension.cpp
+++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp
@@ -40,6 +40,11 @@
#include <tools/applecodesignutils.h>
#endif
+#ifdef Q_OS_WIN
+#include <tools/msvcinfo.h>
+#include <tools/vsenvironmentdetector.h>
+#endif
+
#include <QCryptographicHash>
#include <QDir>
#include <QFileInfo>
@@ -65,6 +70,7 @@ public:
static QScriptValue js_smimeMessageContent(QScriptContext *context, QScriptEngine *engine);
static QScriptValue js_certificateInfo(QScriptContext *context, QScriptEngine *engine);
static QScriptValue js_signingIdentities(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine);
};
void initializeJsExtensionUtilities(QScriptValue extensionObject)
@@ -90,6 +96,8 @@ void initializeJsExtensionUtilities(QScriptValue extensionObject)
engine->newFunction(UtilitiesExtension::js_certificateInfo, 1));
environmentObj.setProperty(QStringLiteral("signingIdentities"),
engine->newFunction(UtilitiesExtension::js_signingIdentities, 0));
+ environmentObj.setProperty(QStringLiteral("msvcCompilerInfo"),
+ engine->newFunction(UtilitiesExtension::js_msvcCompilerInfo, 1));
extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj);
}
@@ -241,6 +249,43 @@ QScriptValue UtilitiesExtension::js_signingIdentities(QScriptContext *context,
#endif
}
+QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine)
+{
+#ifndef Q_OS_WIN
+ Q_UNUSED(engine);
+ return context->throwError(QScriptContext::UnknownError,
+ QLatin1String("msvcCompilerInfo is not available on this platform"));
+#else
+ if (Q_UNLIKELY(context->argumentCount() != 1))
+ return context->throwError(QScriptContext::SyntaxError,
+ QLatin1String("msvcCompilerInfo expects 1 argument"));
+
+ const QString compilerFilePath = context->argument(0).toString();
+ MSVC msvc(compilerFilePath);
+ VsEnvironmentDetector envdetector(&msvc);
+ if (!envdetector.start())
+ return context->throwError(QScriptContext::UnknownError,
+ QStringLiteral("Detecting the MSVC build environment failed: ")
+ + envdetector.errorString());
+
+ try {
+ const auto env = msvc.environments[msvc.architectures.first()];
+
+ QVariantMap envMap;
+ for (const QString &key : env.keys())
+ envMap.insert(key, env.value(key));
+
+ return engine->toScriptValue(QVariantMap {
+ {QStringLiteral("buildEnvironment"), envMap},
+ {QStringLiteral("macros"), msvc.compilerDefines(compilerFilePath)},
+ });
+ } catch (const qbs::ErrorInfo &info) {
+ return context->throwError(QScriptContext::UnknownError,
+ info.toString());
+ }
+#endif
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp
new file mode 100644
index 000000000..0ea4e11f2
--- /dev/null
+++ b/src/lib/corelib/tools/msvcinfo.cpp
@@ -0,0 +1,206 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "msvcinfo.h"
+
+#include <tools/error.h>
+#include <tools/profile.h>
+#include <tools/version.h>
+#include <tools/vsenvironmentdetector.h>
+
+#include <QByteArray>
+#include <QDir>
+#include <QProcess>
+#include <QScopedPointer>
+#include <QStringList>
+#include <QTemporaryFile>
+
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#endif
+
+using namespace qbs;
+using namespace qbs::Internal;
+
+static QString mkStr(const char *s) { return QString::fromLocal8Bit(s); }
+static QString mkStr(const QByteArray &ba) { return mkStr(ba.constData()); }
+
+class TemporaryEnvChanger
+{
+public:
+ TemporaryEnvChanger(const QProcessEnvironment &envChanges)
+ {
+ QProcessEnvironment currentEnv = QProcessEnvironment::systemEnvironment();
+ foreach (const QString &key, envChanges.keys()) {
+ m_changesToRestore.insert(key, currentEnv.value(key));
+ qputenv(qPrintable(key), qPrintable(envChanges.value(key)));
+ }
+ }
+
+ ~TemporaryEnvChanger()
+ {
+ foreach (const QString &key, m_changesToRestore.keys())
+ qputenv(qPrintable(key), qPrintable(m_changesToRestore.value(key)));
+ }
+
+private:
+ QProcessEnvironment m_changesToRestore;
+};
+
+static QByteArray runProcess(const QString &exeFilePath, const QStringList &args,
+ const QProcessEnvironment &env = QProcessEnvironment(),
+ bool allowFailure = false)
+{
+ TemporaryEnvChanger envChanger(env);
+ QProcess process;
+ process.start(exeFilePath, args);
+ if (!process.waitForStarted() || !process.waitForFinished()
+ || process.exitStatus() != QProcess::NormalExit) {
+ throw ErrorInfo(mkStr("Could not run %1 (%2)").arg(exeFilePath, process.errorString()));
+ }
+ if (process.exitCode() != 0 && !allowFailure) {
+ ErrorInfo e(mkStr("Process '%1' failed with exit code %2.")
+ .arg(exeFilePath).arg(process.exitCode()));
+ const QByteArray stdErr = process.readAllStandardError();
+ if (!stdErr.isEmpty())
+ e.append(mkStr("stderr was: %1").arg(mkStr(stdErr)));
+ const QByteArray stdOut = process.readAllStandardOutput();
+ if (!stdOut.isEmpty())
+ e.append(mkStr("stdout was: %1").arg(mkStr(stdOut)));
+ throw e;
+ }
+ return process.readAllStandardOutput().trimmed();
+}
+
+class DummyFile {
+public:
+ DummyFile(const QString &fp) : filePath(fp) { }
+ ~DummyFile() { QFile::remove(filePath); }
+ const QString filePath;
+};
+
+static QStringList parseCommandLine(const QString &commandLine)
+{
+ QStringList list;
+#ifdef Q_OS_WIN
+ wchar_t *buf = new wchar_t[commandLine.size() + 1];
+ buf[commandLine.toWCharArray(buf)] = 0;
+ int argCount = 0;
+ LPWSTR *args = CommandLineToArgvW(buf, &argCount);
+ if (!args)
+ throw ErrorInfo(mkStr("Could not parse command line arguments: ") + commandLine);
+ for (int i = 0; i < argCount; ++i)
+ list.append(QString::fromWCharArray(args[i]));
+ delete[] buf;
+#else
+ Q_UNUSED(commandLine);
+#endif
+ return list;
+}
+
+static QVariantMap getMsvcDefines(const QString &hostCompilerFilePath,
+ const QString &compilerFilePath,
+ const QProcessEnvironment &compilerEnv)
+{
+ const QScopedPointer<QTemporaryFile> dummyFile(
+ new QTemporaryFile(QDir::tempPath() + QLatin1String("/qbs_dummy")));
+ if (!dummyFile->open()) {
+ throw ErrorInfo(mkStr("Could not create temporary file (%1)")
+ .arg(dummyFile->errorString()));
+ }
+ dummyFile->write("#include <stdio.h>\n");
+ dummyFile->write("#include <stdlib.h>\n");
+ dummyFile->write("int main(void) { char *p = getenv(\"MSC_CMD_FLAGS\");"
+ "if (p) printf(\"%s\", p); return EXIT_FAILURE; }\n");
+ dummyFile->close();
+
+ // We cannot use the temporary file itself, as Qt has a lock on it
+ // even after it was closed, causing a "Permission denied" message from MSVC.
+ const QString actualDummyFilePath = dummyFile->fileName() + QLatin1String(".1");
+ const QString nativeDummyFilePath = QDir::toNativeSeparators(actualDummyFilePath);
+ if (!QFile::copy(dummyFile->fileName(), actualDummyFilePath)) {
+ throw ErrorInfo(mkStr("Could not create source '%1' file for compiler.")
+ .arg(nativeDummyFilePath));
+ }
+ DummyFile actualDummyFile(actualDummyFilePath);
+ const QString qbsClFrontend = nativeDummyFilePath + QStringLiteral(".exe");
+
+ // The host compiler is the x86 compiler, which will execute on any edition of Windows
+ // for which host compilers have been released so far (x86, x86_64, ia64)
+ MSVC msvc2(hostCompilerFilePath);
+ VsEnvironmentDetector envdetector(&msvc2);
+ if (!envdetector.start())
+ throw ErrorInfo(QStringLiteral("Detecting the MSVC build environment failed: ")
+ + envdetector.errorString());
+ runProcess(hostCompilerFilePath, QStringList()
+ << QStringLiteral("/nologo")
+ << QStringLiteral("/TC")
+ << nativeDummyFilePath
+ << QStringLiteral("/link")
+ << (QStringLiteral("/out:") + qbsClFrontend), msvc2.environments[QString()]);
+
+ QStringList out = QString::fromLocal8Bit(runProcess(compilerFilePath, QStringList()
+ << QStringLiteral("/nologo")
+ << QStringLiteral("/B1")
+ << qbsClFrontend
+ << QStringLiteral("/c")
+ << QStringLiteral("/TC")
+ << QStringLiteral("NUL"), compilerEnv, true)).split(QStringLiteral("\r\n"));
+
+ if (out.size() != 2)
+ throw ErrorInfo(QStringLiteral("Unexpected compiler frontend output: ")
+ + out.join(QLatin1Char('\n')));
+
+ if (out.first() == QStringLiteral("NUL"))
+ out.removeFirst();
+
+ QVariantMap map;
+ const QStringList args = parseCommandLine(out.first());
+ for (const QString &arg : args) {
+ if (!arg.startsWith(QStringLiteral("-D")))
+ continue;
+ int idx = arg.indexOf(QLatin1Char('='), 2);
+ if (idx > 2)
+ map.insert(arg.mid(2, idx - 2), arg.mid(idx + 1));
+ else
+ map.insert(arg.mid(2), QVariant());
+ }
+
+ return map;
+}
+
+QVariantMap MSVC::compilerDefines(const QString &compilerFilePath) const
+{
+ // Should never happen
+ if (architectures.size() != 1)
+ throw ErrorInfo(mkStr("Unexpected number of architectures"));
+
+ return getMsvcDefines(clPath(), compilerFilePath, environments[architectures.first()]);
+}
diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h
new file mode 100644
index 000000000..8c5c288b6
--- /dev/null
+++ b/src/lib/corelib/tools/msvcinfo.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_MSVCINFO_H
+#define QBS_MSVCINFO_H
+
+#include <logging/translator.h>
+#include <tools/error.h>
+
+#include <QDir>
+#include <QFileInfo>
+#include <QHash>
+#include <QProcessEnvironment>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+class Version;
+
+class MSVC
+{
+public:
+ QString version;
+ QString installPath;
+ QString pathPrefix;
+ QStringList architectures;
+
+ typedef QHash<QString, QProcessEnvironment> EnvironmentPerArch;
+ EnvironmentPerArch environments;
+
+ MSVC() { }
+
+ MSVC(const QString &clPath)
+ {
+ QDir parentDir = QFileInfo(clPath).dir();
+ QString arch = parentDir.dirName().toLower();
+ if (arch == QLatin1String("bin"))
+ arch = QString(); // x86
+ else
+ parentDir.cdUp();
+ architectures << arch;
+ installPath = parentDir.path();
+ }
+
+ QString clPath(const QString &arch = QString()) const {
+ return QDir::cleanPath(
+ installPath + QLatin1Char('/') +
+ pathPrefix + QLatin1Char('/') +
+ arch + QLatin1Char('/') +
+ QLatin1String("cl.exe"));
+ }
+
+ QBS_EXPORT QVariantMap compilerDefines(const QString &compilerFilePath) const;
+};
+
+class WinSDK : public MSVC
+{
+public:
+ bool isDefault;
+
+ WinSDK()
+ {
+ pathPrefix = QLatin1String("bin");
+ }
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_MSVCINFO_H
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
index 915ac750b..3fe070575 100644
--- a/src/lib/corelib/tools/tools.pri
+++ b/src/lib/corelib/tools/tools.pri
@@ -13,6 +13,7 @@ HEADERS += \
$$PWD/filetime.h \
$$PWD/generateoptions.h \
$$PWD/id.h \
+ $$PWD/msvcinfo.h \
$$PWD/persistence.h \
$$PWD/scannerpluginmanager.h \
$$PWD/scripttools.h \
@@ -40,7 +41,8 @@ HEADERS += \
$$PWD/qttools.h \
$$PWD/settingscreator.h \
$$PWD/version.h \
- $$PWD/visualstudioversioninfo.h
+ $$PWD/visualstudioversioninfo.h \
+ $$PWD/vsenvironmentdetector.h
SOURCES += \
$$PWD/architectures.cpp \
@@ -52,6 +54,7 @@ SOURCES += \
$$PWD/fileinfo.cpp \
$$PWD/generateoptions.cpp \
$$PWD/id.cpp \
+ $$PWD/msvcinfo.cpp \
$$PWD/persistence.cpp \
$$PWD/scannerpluginmanager.cpp \
$$PWD/scripttools.cpp \
@@ -74,7 +77,8 @@ SOURCES += \
$$PWD/settingscreator.cpp \
$$PWD/toolchains.cpp \
$$PWD/version.cpp \
- $$PWD/visualstudioversioninfo.cpp
+ $$PWD/visualstudioversioninfo.cpp \
+ $$PWD/vsenvironmentdetector.cpp
osx {
HEADERS += $$PWD/applecodesignutils.h
diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp
new file mode 100644
index 000000000..1958d2c23
--- /dev/null
+++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "vsenvironmentdetector.h"
+
+#include "msvcinfo.h"
+#include <logging/translator.h>
+#include <tools/qbsassert.h>
+
+#include <QDebug>
+#include <QDir>
+#include <QProcess>
+#include <QTemporaryFile>
+#include <QTextStream>
+
+#ifdef Q_OS_WIN
+#include <qt_windows.h>
+#include <ShlObj.h>
+#endif
+
+namespace qbs {
+namespace Internal {
+
+static QString windowsSystem32Path()
+{
+#ifdef Q_OS_WIN
+ wchar_t str[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, 0, str)))
+ return QString::fromUtf16(reinterpret_cast<ushort*>(str));
+#endif
+ return QString();
+}
+
+VsEnvironmentDetector::VsEnvironmentDetector(MSVC *msvc)
+ : m_msvc(msvc)
+ , m_windowsSystemDirPath(windowsSystem32Path())
+{
+}
+
+bool VsEnvironmentDetector::start()
+{
+ m_msvc->environments.clear();
+ const QString vcvarsallbat = QDir::cleanPath(m_msvc->installPath
+ + QLatin1String("/../vcvarsall.bat"));
+ if (!QFile::exists(vcvarsallbat)) {
+ m_errorString = Tr::tr("Cannot find '%1'.").arg(QDir::toNativeSeparators(vcvarsallbat));
+ return false;
+ }
+
+ QTemporaryFile tmpFile(QDir::tempPath() + QLatin1Char('/') + QLatin1String("XXXXXX.bat"));
+ if (!tmpFile.open()) {
+ m_errorString = Tr::tr("Cannot open temporary file '%1' for writing.").arg(
+ tmpFile.fileName());
+ return false;
+ }
+
+ writeBatchFile(&tmpFile, vcvarsallbat);
+ tmpFile.flush();
+
+ QProcess process;
+ const QString shellFilePath = QLatin1String("cmd.exe");
+ process.start(shellFilePath, QStringList()
+ << QLatin1String("/C") << tmpFile.fileName());
+ if (!process.waitForStarted()) {
+ m_errorString = Tr::tr("Failed to start '%1'.").arg(shellFilePath);
+ return false;
+ }
+ process.waitForFinished(-1);
+ if (process.exitStatus() != QProcess::NormalExit) {
+ m_errorString = Tr::tr("Process '%1' did not exit normally.").arg(shellFilePath);
+ return false;
+ }
+ if (process.exitCode() != 0) {
+ m_errorString = Tr::tr("Failed to detect Visual Studio environment.");
+ return false;
+ }
+ parseBatOutput(process.readAllStandardOutput());
+ return true;
+}
+
+static void batClearVars(QTextStream &s, const QStringList &varnames)
+{
+ foreach (const QString &varname, varnames)
+ s << "set " << varname << '=' << endl;
+}
+
+static void batPrintVars(QTextStream &s, const QStringList &varnames)
+{
+ foreach (const QString &varname, varnames)
+ s << "echo " << varname << "=%" << varname << '%' << endl;
+}
+
+static QString vcArchitecture(MSVC *msvc, const QString &targetArch)
+{
+ QString vcArch = targetArch;
+ if (targetArch == QLatin1String("armv7"))
+ vcArch = QLatin1String("arm");
+ if (targetArch == QLatin1String("x86_64"))
+ vcArch = QLatin1String("amd64");
+
+ // Empty string for the native compiler (preferred)
+ for (const QString &hostPrefix :
+ QStringList({QString(), QStringLiteral("amd64_"), QStringLiteral("x86_")})) {
+ if (QFile::exists(msvc->clPath(hostPrefix + vcArch))) {
+ vcArch.prepend(hostPrefix);
+ break;
+ }
+ }
+
+ return vcArch;
+}
+
+void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcvarsallbat) const
+{
+ const QStringList varnames = QStringList() << QLatin1String("PATH")
+ << QLatin1String("INCLUDE") << QLatin1String("LIB");
+ QTextStream s(device);
+ s << "@echo off" << endl;
+ foreach (const QString &architecture, m_msvc->architectures) {
+ s << "echo --" << architecture << "--" << endl
+ << "setlocal" << endl;
+ batClearVars(s, varnames);
+ s << "set PATH=" << m_windowsSystemDirPath << endl; // vcvarsall.bat needs tools from here
+ s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(m_msvc, architecture)
+ << " || exit /b 1" << endl;
+ batPrintVars(s, varnames);
+ s << "endlocal" << endl;
+ }
+}
+
+void VsEnvironmentDetector::parseBatOutput(const QByteArray &output)
+{
+ QString arch;
+ QProcessEnvironment *targetEnv = 0;
+ foreach (QByteArray line, output.split('\n')) {
+ line = line.trimmed();
+ if (line.isEmpty())
+ continue;
+
+ if (line.startsWith("--") && line.endsWith("--")) {
+ line.remove(0, 2);
+ line.chop(2);
+ arch = QString::fromLocal8Bit(line);
+ targetEnv = &m_msvc->environments[arch];
+ } else {
+ int idx = line.indexOf('=');
+ if (idx < 0)
+ continue;
+ QBS_CHECK(targetEnv);
+ const QString name = QString::fromLocal8Bit(line.left(idx));
+ QString value = QString::fromLocal8Bit(line.mid(idx + 1));
+ if (name.compare(QLatin1String("PATH"), Qt::CaseInsensitive) == 0)
+ value.remove(m_windowsSystemDirPath);
+ if (value.endsWith(QLatin1Char(';')))
+ value.chop(1);
+ if (value.endsWith(QLatin1Char('\\')))
+ value.chop(1);
+ targetEnv->insert(name, value);
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h
new file mode 100644
index 000000000..d9856402d
--- /dev/null
+++ b/src/lib/corelib/tools/vsenvironmentdetector.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** 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 http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QBS_VSENVIRONMENTDETECTOR_H
+#define QBS_VSENVIRONMENTDETECTOR_H
+
+#include "qbs_export.h"
+
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+class MSVC;
+
+class QBS_EXPORT VsEnvironmentDetector
+{
+public:
+ VsEnvironmentDetector(MSVC *msvc);
+
+ bool start();
+ QString errorString() const { return m_errorString; }
+
+private:
+ void writeBatchFile(QIODevice *device, const QString &vcvarsallbat) const;
+ void parseBatOutput(const QByteArray &output);
+
+ MSVC *m_msvc;
+ const QString m_windowsSystemDirPath;
+ QString m_errorString;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_VSENVIRONMENTDETECTOR_H