diff options
author | Joerg Bornemann <joerg.bornemann@qt.io> | 2016-11-07 18:16:51 +0100 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2016-11-10 15:13:21 +0000 |
commit | 684548e2634ca620ae24b73f7715b70ac90cbb76 (patch) | |
tree | 44f4b7d5716cf8b8ac896646eb3aa2b5223b95ca | |
parent | 171a104ec567d660fd3ac44cfc2031ac748f6d56 (diff) |
Refactor MSVC toolchain detection
Have one MSVC object per MSVC installation instead of one MSVC per VS
installation with some list members. This cleans up the code quite a bit
and prepares for VS 15 support and speed improvements.
Change-Id: Idf6759e5b05584532f56be4036c87df310363bd3
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r-- | src/app/qbs-setup-toolchains/msvcprobe.cpp | 99 | ||||
-rw-r--r-- | src/lib/corelib/jsextensions/utilitiesextension.cpp | 10 | ||||
-rw-r--r-- | src/lib/corelib/tools/msvcinfo.cpp | 27 | ||||
-rw-r--r-- | src/lib/corelib/tools/msvcinfo.h | 34 | ||||
-rw-r--r-- | src/lib/corelib/tools/vsenvironmentdetector.cpp | 69 | ||||
-rw-r--r-- | src/lib/corelib/tools/vsenvironmentdetector.h | 12 |
6 files changed, 141 insertions, 110 deletions
diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index 7ec15e44a..08d92319a 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -71,8 +71,8 @@ static void setQtHelperProperties(Profile &p, const QString &architecture, const QString &compilerFilePath) { MSVC msvc(compilerFilePath); - VsEnvironmentDetector envdetector(&msvc); - if (!envdetector.start()) { + VsEnvironmentDetector envdetector; + if (!envdetector.start(&msvc)) { qbsWarning() << (QStringLiteral("Detecting the MSVC build environment failed: ") + envdetector.errorString()); return; @@ -95,32 +95,22 @@ static void setQtHelperProperties(Profile &p, const QString &architecture, } } -static void addMSVCPlatform(Settings *settings, QList<Profile> &profiles, - QString name, const QString &installPath, const QString &architecture, - bool appendArchToName = true) +static void addMSVCPlatform(Settings *settings, QList<Profile> &profiles, QString name, MSVC *msvc) { - QStringList toolchainInstallPath = QStringList() << installPath; - if (!architecture.isEmpty()) { - toolchainInstallPath.append(architecture); - if (appendArchToName) - name.append(QLatin1Char('-') + architecture); - } qbsInfo() << Tr::tr("Setting up profile '%1'.").arg(name); Profile p(name, settings); p.removeProfile(); p.setValue(QLatin1String("qbs.targetOS"), QStringList(QLatin1String("windows"))); p.setValue(QLatin1String("qbs.toolchain"), QStringList(QLatin1String("msvc"))); - p.setValue(QLatin1String("cpp.toolchainInstallPath"), - toolchainInstallPath.join(QDir::separator())); - setQtHelperProperties(p, architecture, - toolchainInstallPath.join(QDir::separator()) + QLatin1String("/cl.exe")); + p.setValue(QLatin1String("cpp.toolchainInstallPath"), msvc->binPath); + setQtHelperProperties(p, msvc->architecture, msvc->binPath + QLatin1String("/cl.exe")); profiles << p; } -static void findSupportedArchitectures(MSVC *msvc) +static QStringList findSupportedArchitectures(MSVC *msvc) { static const QStringList knownArchitectures = QStringList() - << QString() // x86_x86 + << QStringLiteral("x86") << QStringLiteral("amd64_x86") << QStringLiteral("amd64") << QStringLiteral("x86_amd64") @@ -128,10 +118,12 @@ static void findSupportedArchitectures(MSVC *msvc) << QStringLiteral("x86_ia64") << QStringLiteral("x86_arm") << QStringLiteral("amd64_arm"); + QStringList result; for (const QString &knownArchitecture : knownArchitectures) { - if (QFile::exists(msvc->clPath(knownArchitecture))) - msvc->architectures += knownArchitecture; + if (QFile::exists(msvc->clPathForArchitecture(knownArchitecture))) + result += knownArchitecture; } + return result; } static QString wow6432Key() @@ -159,24 +151,26 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) foreach (const QString &sdkKey, sdkRegistry.childGroups()) { WinSDK sdk; sdk.version = sdkKey; - sdk.installPath = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString(); - sdk.isDefault = (sdk.installPath == defaultSdkPath); - if (sdk.installPath.isEmpty()) + sdk.vcInstallPath = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString(); + sdk.isDefault = (sdk.vcInstallPath == defaultSdkPath); + if (sdk.vcInstallPath.isEmpty()) continue; - if (sdk.installPath.endsWith(QLatin1Char('\\'))) - sdk.installPath.chop(1); - findSupportedArchitectures(&sdk); + if (sdk.vcInstallPath.endsWith(QLatin1Char('\\'))) + sdk.vcInstallPath.chop(1); if (sdk.isDefault) defaultWinSDK = sdk; - winSDKs += sdk; + foreach (const QString &arch, findSupportedArchitectures(&sdk)) { + WinSDK specificSDK = sdk; + specificSDK.architecture = arch; + specificSDK.binPath = specificSDK.binPathForArchitecture(arch); + winSDKs += specificSDK; + } } } foreach (const WinSDK &sdk, winSDKs) { qbsInfo() << Tr::tr(" Windows SDK %1 detected:\n" - " installed in %2").arg(sdk.version, sdk.installPath); - if (!sdk.architectures.isEmpty()) - qbsInfo() << Tr::tr(" This SDK contains C++ compiler(s)."); + " installed in %2").arg(sdk.version, sdk.vcInstallPath); if (sdk.isDefault) qbsInfo() << Tr::tr(" This is the default SDK on this machine."); } @@ -194,11 +188,10 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) continue; MSVC msvc; - msvc.installPath = vsRegistry.value(vsName).toString(); - if (!msvc.installPath.endsWith(QLatin1Char('\\'))) - msvc.installPath += QLatin1Char('\\'); - msvc.installPath += QLatin1String("bin"); - findSupportedArchitectures(&msvc); + msvc.vcInstallPath = vsRegistry.value(vsName).toString(); + if (!msvc.vcInstallPath.endsWith(QLatin1Char('\\'))) + msvc.vcInstallPath += QLatin1Char('\\'); + msvc.vcInstallPath += QLatin1String("bin"); msvc.version = QString::number(Internal::VisualStudioVersionInfo( Internal::Version::fromString(vsName)).marketingVersion()); @@ -208,17 +201,22 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) } // Check existence of various install scripts - const QString vcvars32bat = msvc.installPath + QLatin1String("/vcvars32.bat"); + const QString vcvars32bat = msvc.vcInstallPath + QLatin1String("/vcvars32.bat"); if (!QFileInfo(vcvars32bat).isFile()) continue; - msvcs += msvc; + foreach (const QString &arch, findSupportedArchitectures(&msvc)) { + MSVC specificMSVC = msvc; + specificMSVC.architecture = arch; + specificMSVC.binPath = specificMSVC.binPathForArchitecture(arch); + msvcs += specificMSVC; + } } foreach (const MSVC &msvc, msvcs) { - qbsInfo() << Tr::tr(" MSVC detected:\n" - " version %1\n" - " installed in %2").arg(msvc.version, msvc.installPath); + qbsInfo() << Tr::tr(" MSVC %1 (%2) detected in\n" + " %3").arg(msvc.version, msvc.architecture, + QDir::toNativeSeparators(msvc.binPath)); } if (winSDKs.isEmpty() && msvcs.isEmpty()) { @@ -227,18 +225,18 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) return; } - foreach (const WinSDK &sdk, winSDKs) { - foreach (const QString &arch, sdk.architectures) { - addMSVCPlatform(settings, profiles, QLatin1String("WinSDK") + sdk.version, - sdk.installPath + QLatin1String("\\bin"), arch); - } + for (int i = 0; i < winSDKs.count(); ++i) { + WinSDK &sdk = winSDKs[i]; + const QString name = QLatin1String("WinSDK") + sdk.version + QLatin1Char('-') + + sdk.architecture; + addMSVCPlatform(settings, profiles, name, &sdk); } - foreach (const MSVC &msvc, msvcs) { - foreach (const QString &arch, msvc.architectures) { - addMSVCPlatform(settings, profiles, QLatin1String("MSVC") + msvc.version, - msvc.installPath, arch); - } + for (int i = 0; i < msvcs.count(); ++i) { + MSVC &msvc = msvcs[i]; + const QString name = QLatin1String("MSVC") + msvc.version + QLatin1Char('-') + + msvc.architecture; + addMSVCPlatform(settings, profiles, name, &msvc); } } @@ -247,8 +245,7 @@ void createMsvcProfile(const QString &profileName, const QString &compilerFilePa { MSVC msvc(compilerFilePath); QList<Profile> dummy; - addMSVCPlatform(settings, dummy, profileName, msvc.installPath, - msvc.architectures.first(), false); + addMSVCPlatform(settings, dummy, profileName, &msvc); qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") .arg(profileName, QDir::toNativeSeparators(compilerFilePath)); } diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index e8ca1ffe0..1463438fc 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -274,18 +274,16 @@ QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QS const QString compilerFilePath = context->argument(0).toString(); MSVC msvc(compilerFilePath); - VsEnvironmentDetector envdetector(&msvc); - if (!envdetector.start()) + VsEnvironmentDetector envdetector; + if (!envdetector.start(&msvc)) 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)); + for (const QString &key : msvc.environment.keys()) + envMap.insert(key, msvc.environment.value(key)); return engine->toScriptValue(QVariantMap { {QStringLiteral("buildEnvironment"), envMap}, diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index ce3a540a4..e2862a591 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -167,8 +167,8 @@ static QVariantMap getMsvcDefines(const QString &hostCompilerFilePath, // 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()) + VsEnvironmentDetector envdetector; + if (!envdetector.start(&msvc2)) throw ErrorInfo(QStringLiteral("Detecting the MSVC build environment failed: ") + envdetector.errorString()); runProcess(hostCompilerFilePath, QStringList() @@ -177,7 +177,7 @@ static QVariantMap getMsvcDefines(const QString &hostCompilerFilePath, << (QStringLiteral("/Fo") + qbsClFrontendObj) << nativeDummyFilePath << QStringLiteral("/link") - << (QStringLiteral("/out:") + qbsClFrontend), msvc2.environments[QString()]); + << (QStringLiteral("/out:") + qbsClFrontend), msvc2.environment); QStringList out = QString::fromLocal8Bit(runProcess(compilerFilePath, QStringList() << QStringLiteral("/nologo") @@ -209,11 +209,22 @@ static QVariantMap getMsvcDefines(const QString &hostCompilerFilePath, return map; } -QVariantMap MSVC::compilerDefines(const QString &compilerFilePath) const +QString MSVC::binPathForArchitecture(const QString &arch) const { - // Should never happen - if (architectures.size() != 1) - throw ErrorInfo(mkStr("Unexpected number of architectures")); + QString archSubDir; + if (arch != QStringLiteral("x86")) + archSubDir = arch; + return QDir::cleanPath(vcInstallPath + QLatin1Char('/') + pathPrefix + QLatin1Char('/') + + archSubDir); +} - return getMsvcDefines(clPath(), compilerFilePath, environments[architectures.first()]); +QString MSVC::clPathForArchitecture(const QString &arch) const +{ + return binPathForArchitecture(arch) + QLatin1String("/cl.exe"); +} + +QVariantMap MSVC::compilerDefines(const QString &compilerFilePath) const +{ + return getMsvcDefines(clPathForArchitecture(QStringLiteral("x86")), compilerFilePath, + environment); } diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h index 8b8f16d6b..9e9c68cf3 100644 --- a/src/lib/corelib/tools/msvcinfo.h +++ b/src/lib/corelib/tools/msvcinfo.h @@ -54,39 +54,37 @@ namespace Internal { class Version; +/** + * Represents one MSVC installation for one specific target architecture. + * There are potentially multiple MSVCs in one Visual Studio installation. + */ class MSVC { public: QString version; - QString installPath; + QString vcInstallPath; + QString binPath; QString pathPrefix; - QStringList architectures; - - typedef QHash<QString, QProcessEnvironment> EnvironmentPerArch; - EnvironmentPerArch environments; + QString architecture; + QProcessEnvironment environment; MSVC() { } MSVC(const QString &clPath) { QDir parentDir = QFileInfo(clPath).dir(); - QString arch = parentDir.dirName().toLower(); - if (arch == QLatin1String("bin")) - arch = QString(); // x86 + QString parentDirName = parentDir.dirName().toLower(); + if (parentDirName == QLatin1String("bin")) + parentDirName = QStringLiteral("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")); + architecture = parentDirName; + vcInstallPath = parentDir.path(); + binPath = vcInstallPath; } + QBS_EXPORT QString binPathForArchitecture(const QString &arch) const; + QBS_EXPORT QString clPathForArchitecture(const QString &arch) const; QBS_EXPORT QVariantMap compilerDefines(const QString &compilerFilePath) const; }; diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp index 5617e0bd7..c4db89fec 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.cpp +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -39,7 +39,6 @@ #include "vsenvironmentdetector.h" -#include "msvcinfo.h" #include <logging/translator.h> #include <tools/qbsassert.h> @@ -67,16 +66,41 @@ static QString windowsSystem32Path() return QString(); } -VsEnvironmentDetector::VsEnvironmentDetector(MSVC *msvc) - : m_msvc(msvc) - , m_windowsSystemDirPath(windowsSystem32Path()) +VsEnvironmentDetector::VsEnvironmentDetector() + : m_windowsSystemDirPath(windowsSystem32Path()) { } -bool VsEnvironmentDetector::start() +bool VsEnvironmentDetector::start(MSVC *msvc) { - m_msvc->environments.clear(); - const QString vcvarsallbat = QDir::cleanPath(m_msvc->installPath + return start(QVector<MSVC *>() << msvc); +} + +bool VsEnvironmentDetector::start(QVector<MSVC *> msvcs) +{ + std::sort(msvcs.begin(), msvcs.end(), [] (const MSVC *a, const MSVC *b) -> bool { + return a->vcInstallPath < b->vcInstallPath; + }); + + QVector<MSVC *> compatibleMSVCs; + QString lastVcInstallPath; + foreach (MSVC *msvc, msvcs) { + if (lastVcInstallPath != msvc->vcInstallPath) { + lastVcInstallPath = msvc->vcInstallPath; + if (!compatibleMSVCs.isEmpty()) { + startDetection(compatibleMSVCs); + compatibleMSVCs.clear(); + } + } + compatibleMSVCs.append(msvc); + } + startDetection(compatibleMSVCs); + return true; +} + +bool VsEnvironmentDetector::startDetection(const QVector<MSVC *> &compatibleMSVCs) +{ + const QString vcvarsallbat = QDir::cleanPath(compatibleMSVCs.first()->vcInstallPath + QLatin1String("/../vcvarsall.bat")); if (!QFile::exists(vcvarsallbat)) { m_errorString = Tr::tr("Cannot find '%1'.").arg(QDir::toNativeSeparators(vcvarsallbat)); @@ -90,7 +114,7 @@ bool VsEnvironmentDetector::start() return false; } - writeBatchFile(&tmpFile, vcvarsallbat); + writeBatchFile(&tmpFile, vcvarsallbat, compatibleMSVCs); tmpFile.flush(); QProcess process; @@ -110,8 +134,9 @@ bool VsEnvironmentDetector::start() m_errorString = Tr::tr("Failed to detect Visual Studio environment."); return false; } - parseBatOutput(process.readAllStandardOutput()); + parseBatOutput(process.readAllStandardOutput(), compatibleMSVCs); return true; + } static void batClearVars(QTextStream &s, const QStringList &varnames) @@ -126,18 +151,17 @@ static void batPrintVars(QTextStream &s, const QStringList &varnames) s << "echo " << varname << "=%" << varname << '%' << endl; } -static QString vcArchitecture(MSVC *msvc, const QString &targetArch) +static QString vcArchitecture(const MSVC *msvc) { - QString vcArch = targetArch; - if (targetArch == QLatin1String("armv7")) + QString vcArch = msvc->architecture; + if (msvc->architecture == QLatin1String("armv7")) vcArch = QLatin1String("arm"); - if (targetArch == QLatin1String("x86_64")) + if (msvc->architecture == 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))) { + QStringList({QStringLiteral("x86"), QStringLiteral("amd64_"), QStringLiteral("x86_")})) { + if (QFile::exists(msvc->clPathForArchitecture(hostPrefix + vcArch))) { vcArch.prepend(hostPrefix); break; } @@ -146,25 +170,26 @@ static QString vcArchitecture(MSVC *msvc, const QString &targetArch) return vcArch; } -void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcvarsallbat) const +void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcvarsallbat, + const QVector<MSVC *> &msvcs) 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 + for (const MSVC *msvc : msvcs) { + s << "echo --" << msvc->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) + s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc) << " || exit /b 1" << endl; batPrintVars(s, varnames); s << "endlocal" << endl; } } -void VsEnvironmentDetector::parseBatOutput(const QByteArray &output) +void VsEnvironmentDetector::parseBatOutput(const QByteArray &output, QVector<MSVC *> msvcs) { QString arch; QProcessEnvironment *targetEnv = 0; @@ -177,7 +202,7 @@ void VsEnvironmentDetector::parseBatOutput(const QByteArray &output) line.remove(0, 2); line.chop(2); arch = QString::fromLocal8Bit(line); - targetEnv = &m_msvc->environments[arch]; + targetEnv = &msvcs.takeFirst()->environment; } else { int idx = line.indexOf('='); if (idx < 0) diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h index 00b2cc9c9..114f792ee 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.h +++ b/src/lib/corelib/tools/vsenvironmentdetector.h @@ -41,6 +41,7 @@ #define QBS_VSENVIRONMENTDETECTOR_H #include "qbs_export.h" +#include "msvcinfo.h" #include <QStringList> @@ -56,16 +57,17 @@ class MSVC; class QBS_EXPORT VsEnvironmentDetector { public: - VsEnvironmentDetector(MSVC *msvc); + VsEnvironmentDetector(); - bool start(); + bool start(MSVC *msvc); + bool start(QVector<MSVC *> msvcs); QString errorString() const { return m_errorString; } private: - void writeBatchFile(QIODevice *device, const QString &vcvarsallbat) const; - void parseBatOutput(const QByteArray &output); + bool startDetection(const QVector<MSVC *> &compatibleMSVCs); + void writeBatchFile(QIODevice *device, const QString &vcvarsallbat, const QVector<MSVC *> &msvcs) const; + void parseBatOutput(const QByteArray &output, QVector<MSVC *> msvcs); - MSVC *m_msvc; const QString m_windowsSystemDirPath; QString m_errorString; }; |