diff options
author | Jake Petroules <jake.petroules@qt.io> | 2016-05-22 15:37:18 -0700 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2016-06-27 13:13:52 +0000 |
commit | 129e7a8ab1edfb583157db6050ab3f1bd426279e (patch) | |
tree | 6f3634e0c1ce07501a1368a774ef62fe1106f065 /src/lib | |
parent | 2ea9e28a6963cae217923d77fd00f581306b1980 (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')
-rw-r--r-- | src/lib/corelib/corelib.pro | 2 | ||||
-rw-r--r-- | src/lib/corelib/corelib.qbs | 6 | ||||
-rw-r--r-- | src/lib/corelib/jsextensions/utilitiesextension.cpp | 45 | ||||
-rw-r--r-- | src/lib/corelib/tools/msvcinfo.cpp | 206 | ||||
-rw-r--r-- | src/lib/corelib/tools/msvcinfo.h | 98 | ||||
-rw-r--r-- | src/lib/corelib/tools/tools.pri | 8 | ||||
-rw-r--r-- | src/lib/corelib/tools/vsenvironmentdetector.cpp | 191 | ||||
-rw-r--r-- | src/lib/corelib/tools/vsenvironmentdetector.h | 67 |
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 |