diff options
16 files changed, 290 insertions, 127 deletions
diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js index 670c3894c..b3714b957 100644 --- a/share/qbs/imports/qbs/ModUtils/utils.js +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -515,7 +515,7 @@ function guessArchitecture(m) { } } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86"])) { architecture = "x86"; - } else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64"])) { + } else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64", "_M_AMD64"])) { architecture = "x86_64"; } else if (hasAnyOf(m, ["__ia64", "__ia64__", "_M_IA64"])) { architecture = "ia64"; diff --git a/src/app/qbs-setup-toolchains/compilerversion.h b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs index df9f66045..05a307439 100644 --- a/src/app/qbs-setup-toolchains/compilerversion.h +++ b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** This file is part of Qbs. @@ -27,21 +27,43 @@ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ -#ifndef QBS_COMPILERVERSION_H -#define QBS_COMPILERVERSION_H -#include <QtGlobal> -#include <QProcessEnvironment> +import qbs +import qbs.ModUtils +import qbs.Utilities -namespace qbs { class Profile; } +PathProbe { + // Inputs + property string compilerFilePath + property string preferredArchitecture -QT_BEGIN_NAMESPACE -class QString; -class QStringList; -QT_END_NAMESPACE + // Outputs + property string architecture + property int versionMajor + property int versionMinor + property int versionPatch + property var buildEnv -void setCompilerVersion(const QString &compilerFilePath, const QStringList &qbsToolchain, - qbs::Profile &profile, - const QProcessEnvironment &compilerEnv = QProcessEnvironment()); + configure: { + var info = Utilities.msvcCompilerInfo(compilerFilePath); + found = !!info && !!info.macros && !!info.buildEnvironment; -#endif // Include guard + var macros = info.macros; + architecture = ModUtils.guessArchitecture(macros); + + var ver = macros["_MSC_FULL_VER"]; + + versionMajor = parseInt(ver.substr(0, 2), 10); + versionMinor = parseInt(ver.substr(2, 2), 10); + versionPatch = parseInt(ver.substr(4), 10); + + buildEnv = info.buildEnvironment; + + if (preferredArchitecture && Utilities.canonicalArchitecture(preferredArchitecture) + !== Utilities.canonicalArchitecture(architecture)) { + throw "'" + preferredArchitecture + + "' differs from the architecture produced by this compiler (" + + architecture + ")"; + } + } +} diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 3c0ef0574..433fbc395 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -329,6 +329,14 @@ Module { return !architecture || architecture === Utilities.canonicalArchitecture(architecture); }, "'" + architecture + "' is invalid. You must use the canonical name '" + Utilities.canonicalArchitecture(architecture) + "'"); + validator.setRequiredProperty("compilerVersion", compilerVersion); + validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); + validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); + validator.setRequiredProperty("compilerVersionPatch", compilerVersionPatch); + validator.addVersionValidator("compilerVersion", compilerVersion, 3, 3); + validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); + validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); + validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); if (minimumWindowsVersion) { validator.addVersionValidator("minimumWindowsVersion", minimumWindowsVersion, 2, 2); validator.addCustomValidator("minimumWindowsVersion", minimumWindowsVersion, function (v) { diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index a7cb6b36f..f8974bf89 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -33,6 +33,7 @@ import qbs.File import qbs.FileInfo import qbs.ModUtils import qbs.PathTools +import qbs.Probes import qbs.Utilities import qbs.WindowsUtils import 'msvc.js' as MSVC @@ -44,6 +45,18 @@ CppModule { id: module + Probes.MsvcProbe { + id: msvcProbe + compilerFilePath: compilerPath + preferredArchitecture: qbs.architecture + } + + qbs.architecture: msvcProbe.found ? msvcProbe.architecture : original + + compilerVersionMajor: msvcProbe.versionMajor + compilerVersionMinor: msvcProbe.versionMinor + compilerVersionPatch: msvcProbe.versionPatch + windowsApiCharacterSet: "unicode" platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) platformCommonCompilerFlags: { @@ -55,6 +68,7 @@ CppModule { compilerDefines: ['_WIN32', MSVC.compilerVersionDefine(module)] warningLevel: "default" compilerName: "cl.exe" + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) assemblerName: { switch (qbs.architecture) { case "armv7": @@ -85,6 +99,16 @@ CppModule { property string dynamicLibraryImportSuffix: ".lib" imageFormat: "pe" + property var buildEnv: msvcProbe.buildEnv + + setupBuildEnvironment: { + for (var key in buildEnv) { + var v = new ModUtils.EnvironmentVariable(key); + v.value = buildEnv[key]; + v.set(); + } + } + Rule { condition: useCPrecompiledHeader inputs: ["c_pch_src"] diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index 898c9d4f4..ffaaabc5a 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -30,18 +30,17 @@ #include "msvcprobe.h" -#include "compilerversion.h" -#include "msvcinfo.h" #include "probe.h" -#include "vsenvironmentdetector.h" #include "../shared/logging/consolelogger.h" #include <logging/translator.h> #include <tools/architectures.h> #include <tools/error.h> +#include <tools/msvcinfo.h> #include <tools/profile.h> #include <tools/settings.h> #include <tools/visualstudioversioninfo.h> +#include <tools/vsenvironmentdetector.h> #include <QDir> #include <QFileInfo> @@ -50,6 +49,7 @@ #include <QVector> using namespace qbs; +using namespace qbs::Internal; using Internal::Tr; QT_BEGIN_NAMESPACE @@ -57,46 +57,67 @@ Q_DECLARE_TYPEINFO(WinSDK, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(MSVC, Q_MOVABLE_TYPE); QT_END_NAMESPACE -static void writeEnvironment(Profile &p, const QProcessEnvironment &env) +// Not necessary but helps setup-qt automatically associate base profiles +static void setQtHelperProperties(Profile &p, const QString &architecture, + const QString &compilerFilePath) { - foreach (const QString &name, env.keys()) - p.setValue(QLatin1String("buildEnvironment.") + name, env.value(name)); + MSVC msvc(compilerFilePath); + VsEnvironmentDetector envdetector(&msvc); + if (!envdetector.start()) { + qbsWarning() << (QStringLiteral("Detecting the MSVC build environment failed: ") + + envdetector.errorString()); + return; + } + + QString targetArch = architecture.split(QLatin1Char('_')).last(); + if (targetArch.isEmpty()) + targetArch = QStringLiteral("x86"); + if (targetArch == QStringLiteral("arm")) + targetArch = QStringLiteral("armv7"); + + p.setValue(QStringLiteral("qbs.architecture"), canonicalArchitecture(targetArch)); + p.setValue(QStringLiteral("cpp.compilerVersionMajor"), + msvc.compilerDefines(compilerFilePath)[QStringLiteral("_MSC_FULL_VER")] + .toString().mid(0, 2).toInt()); } -static void addMSVCPlatform(const MSVC &msvc, Settings *settings, QList<Profile> &profiles, +static void addMSVCPlatform(Settings *settings, QList<Profile> &profiles, QString name, const QString &installPath, const QString &architecture, bool appendArchToName = true) { - if (appendArchToName) - name.append(QLatin1Char('_') + architecture); + 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("cpp.toolchainInstallPath"), installPath); p.setValue(QLatin1String("qbs.toolchain"), QStringList(QLatin1String("msvc"))); - p.setValue(QLatin1String("qbs.architecture"), canonicalArchitecture(architecture)); - const QProcessEnvironment compilerEnvironment = msvc.environments.value(architecture); - setCompilerVersion(installPath + QLatin1String("/cl.exe"), QStringList(QLatin1String("msvc")), - p, compilerEnvironment); - writeEnvironment(p, compilerEnvironment); + p.setValue(QLatin1String("cpp.toolchainInstallPath"), + toolchainInstallPath.join(QDir::separator())); + setQtHelperProperties(p, architecture, + toolchainInstallPath.join(QDir::separator()) + QLatin1String("/cl.exe")); profiles << p; } static void findSupportedArchitectures(MSVC *msvc) { - if (QFile::exists(msvc->clPath()) - || QFile::exists(msvc->clPath(QLatin1String("amd64_x86")))) - msvc->architectures += QLatin1String("x86"); - if (QFile::exists(msvc->clPath(QLatin1String("amd64"))) - || QFile::exists(msvc->clPath(QLatin1String("x86_amd64")))) - msvc->architectures += QLatin1String("x86_64"); - if (QFile::exists(msvc->clPath(QLatin1String("ia64"))) - || QFile::exists(msvc->clPath(QLatin1String("x86_ia64")))) - msvc->architectures += QLatin1String("ia64"); - if (QFile::exists(msvc->clPath(QLatin1String("x86_arm"))) - || QFile::exists(msvc->clPath(QLatin1String("amd64_arm")))) - msvc->architectures += QLatin1String("armv7"); + static const QStringList knownArchitectures = QStringList() + << QString() // x86_x86 + << QStringLiteral("amd64_x86") + << QStringLiteral("amd64") + << QStringLiteral("x86_amd64") + << QStringLiteral("ia64") + << QStringLiteral("x86_ia64") + << QStringLiteral("x86_arm") + << QStringLiteral("amd64_arm"); + for (const QString &knownArchitecture : knownArchitectures) { + if (QFile::exists(msvc->clPath(knownArchitecture))) + msvc->architectures += knownArchitecture; + } } static QString wow6432Key() @@ -177,14 +198,6 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) if (!QFileInfo(vcvars32bat).isFile()) continue; - VsEnvironmentDetector envdetector(&msvc); - if (!envdetector.start()) { - qbsError() << " " - << Tr::tr("Detecting the build environment from '%1' failed.").arg( - vcvars32bat); - continue; - } - msvcs += msvc; } @@ -202,14 +215,14 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) foreach (const WinSDK &sdk, winSDKs) { foreach (const QString &arch, sdk.architectures) { - addMSVCPlatform(sdk, settings, profiles, QLatin1String("WinSDK") + sdk.version, + addMSVCPlatform(settings, profiles, QLatin1String("WinSDK") + sdk.version, sdk.installPath + QLatin1String("\\bin"), arch); } } foreach (const MSVC &msvc, msvcs) { foreach (const QString &arch, msvc.architectures) { - addMSVCPlatform(msvc, settings, profiles, QLatin1String("MSVC") + msvc.version, + addMSVCPlatform(settings, profiles, QLatin1String("MSVC") + msvc.version, msvc.installPath, arch); } } @@ -219,11 +232,8 @@ void createMsvcProfile(const QString &profileName, const QString &compilerFilePa Settings *settings) { MSVC msvc(compilerFilePath); - VsEnvironmentDetector envdetector(&msvc); - if (!envdetector.start()) - throw qbs::ErrorInfo(Tr::tr("Detecting the build environment failed.")); QList<Profile> dummy; - addMSVCPlatform(msvc, settings, dummy, profileName, msvc.installPath, + addMSVCPlatform(settings, dummy, profileName, msvc.installPath, msvc.architectures.first(), false); qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") .arg(profileName, QDir::toNativeSeparators(compilerFilePath)); diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index 793c96997..8a5b306f2 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -29,7 +29,6 @@ ****************************************************************************/ #include "probe.h" -#include "compilerversion.h" #include "msvcprobe.h" #include "xcodeprobe.h" @@ -140,7 +139,6 @@ static void setCommonProperties(Profile &profile, const QString &compilerFilePat profile.setValue(QLatin1String("cpp.toolchainInstallPath"), cfi.absolutePath()); profile.setValue(QLatin1String("qbs.toolchain"), toolchainTypes); - setCompilerVersion(compilerFilePath, toolchainTypes, profile); const QString suffix = compilerName.right(compilerName.size() - prefix.size()); if (!standardCompilerFileNames().contains(suffix)) diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro index 865a445ea..f395c5458 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro @@ -4,20 +4,15 @@ TARGET = qbs-setup-toolchains HEADERS += \ commandlineparser.h \ - compilerversion.h \ - msvcinfo.h \ msvcprobe.h \ probe.h \ - vsenvironmentdetector.h \ xcodeprobe.h SOURCES += \ commandlineparser.cpp \ - compilerversion.cpp \ main.cpp \ msvcprobe.cpp \ probe.cpp \ - vsenvironmentdetector.cpp \ xcodeprobe.cpp mingw { diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs index 13bb0dd01..3536b51db 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -6,18 +6,12 @@ QbsApp { files: [ "commandlineparser.cpp", "commandlineparser.h", - "compilerversion.cpp", - "compilerversion.h", "main.cpp", - "msvcinfo.h", "msvcprobe.cpp", "msvcprobe.h", "probe.cpp", "probe.h", - "vsenvironmentdetector.cpp", - "vsenvironmentdetector.h", "xcodeprobe.cpp", - "xcodeprobe.h" + "xcodeprobe.h", ] } - 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/app/qbs-setup-toolchains/compilerversion.cpp b/src/lib/corelib/tools/msvcinfo.cpp index 8de5f3ba5..0ea4e11f2 100644 --- a/src/app/qbs-setup-toolchains/compilerversion.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** This file is part of Qbs. @@ -27,11 +27,13 @@ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ -#include "compilerversion.h" + +#include "msvcinfo.h" #include <tools/error.h> #include <tools/profile.h> #include <tools/version.h> +#include <tools/vsenvironmentdetector.h> #include <QByteArray> #include <QDir> @@ -40,6 +42,10 @@ #include <QStringList> #include <QTemporaryFile> +#ifdef Q_OS_WIN +#include <qt_windows.h> +#endif + using namespace qbs; using namespace qbs::Internal; @@ -69,7 +75,8 @@ private: }; static QByteArray runProcess(const QString &exeFilePath, const QStringList &args, - const QProcessEnvironment &env = QProcessEnvironment()) + const QProcessEnvironment &env = QProcessEnvironment(), + bool allowFailure = false) { TemporaryEnvChanger envChanger(env); QProcess process; @@ -78,7 +85,7 @@ static QByteArray runProcess(const QString &exeFilePath, const QStringList &args || process.exitStatus() != QProcess::NormalExit) { throw ErrorInfo(mkStr("Could not run %1 (%2)").arg(exeFilePath, process.errorString())); } - if (process.exitCode() != 0) { + 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(); @@ -99,8 +106,28 @@ public: const QString filePath; }; -static Version getMsvcVersion(const QString &compilerFilePath, - const QProcessEnvironment &compilerEnv) +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"))); @@ -108,8 +135,10 @@ static Version getMsvcVersion(const QString &compilerFilePath, throw ErrorInfo(mkStr("Could not create temporary file (%1)") .arg(dummyFile->errorString())); } - const QByteArray magicPrefix = "int version = "; - dummyFile->write(magicPrefix + "_MSC_FULL_VER\r\n"); + 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 @@ -121,49 +150,57 @@ static Version getMsvcVersion(const QString &compilerFilePath, .arg(nativeDummyFilePath)); } DummyFile actualDummyFile(actualDummyFilePath); - const QString preprocessedFilePath = nativeDummyFilePath + QLatin1String(".i"); - const QStringList compilerArgs = QStringList() << QLatin1String("/nologo") - << QLatin1String("/P") << nativeDummyFilePath - << (QLatin1String("/Fi") + preprocessedFilePath); - runProcess(compilerFilePath, compilerArgs, compilerEnv); - QFile preprocessedFile(preprocessedFilePath); - if (!preprocessedFile.open(QIODevice::ReadOnly)) { - throw ErrorInfo(mkStr("Cannot read preprocessed file '%1' (%2)") - .arg(preprocessedFilePath, preprocessedFile.errorString())); - } - QString versionString; - foreach (const QByteArray &line, preprocessedFile.readAll().split('\n')) { - const QByteArray cleanLine = line.trimmed(); - if (cleanLine.startsWith(magicPrefix)) { - versionString = QString::fromLocal8Bit(cleanLine.mid(magicPrefix.count())); - break; - } + 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()); } - if (versionString.isEmpty()) - throw ErrorInfo(mkStr("No version number found in preprocessed file.")); - if (versionString.count() < 5) - throw ErrorInfo(mkStr("Version number '%1' not understood.").arg(versionString)); - versionString.insert(2, QLatin1Char('.')).insert(5, QLatin1Char('.')); - const Version version = Version::fromString(versionString); - if (!version.isValid()) - throw ErrorInfo(mkStr("Invalid version string '%1'.").arg(versionString)); - return version; -} + return map; +} -void setCompilerVersion(const QString &compilerFilePath, const QStringList &qbsToolchain, - Profile &profile, const QProcessEnvironment &compilerEnv) +QVariantMap MSVC::compilerDefines(const QString &compilerFilePath) const { - try { - if (qbsToolchain.contains(QLatin1String("msvc"))) { - const Version version = getMsvcVersion(compilerFilePath, compilerEnv); - if (version.isValid()) { - profile.setValue(QLatin1String("cpp.compilerVersionMajor"), version.majorVersion()); - profile.setValue(QLatin1String("cpp.compilerVersionMinor"), version.minorVersion()); - profile.setValue(QLatin1String("cpp.compilerVersionPatch"), version.patchLevel()); - } - } - } catch (const ErrorInfo &e) { - qDebug("Warning: Failed to retrieve compiler version: %s", qPrintable(e.toString())); - } + // 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/app/qbs-setup-toolchains/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h index aab1bb57e..8c5c288b6 100644 --- a/src/app/qbs-setup-toolchains/msvcinfo.h +++ b/src/lib/corelib/tools/msvcinfo.h @@ -40,6 +40,11 @@ #include <QProcessEnvironment> #include <QStringList> +namespace qbs { +namespace Internal { + +class Version; + class MSVC { public: @@ -58,20 +63,22 @@ public: QDir parentDir = QFileInfo(clPath).dir(); QString arch = parentDir.dirName().toLower(); if (arch == QLatin1String("bin")) - arch = QLatin1String("x86"); + arch = QString(); // x86 else parentDir.cdUp(); architectures << arch; installPath = parentDir.path(); } - QString clPath(const QString &arch = QString()) { + 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 @@ -85,4 +92,7 @@ public: } }; +} // 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/app/qbs-setup-toolchains/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp index b754f23ae..1958d2c23 100644 --- a/src/app/qbs-setup-toolchains/vsenvironmentdetector.cpp +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -45,7 +45,8 @@ #include <ShlObj.h> #endif -using qbs::Internal::Tr; +namespace qbs { +namespace Internal { static QString windowsSystem32Path() { @@ -185,3 +186,6 @@ void VsEnvironmentDetector::parseBatOutput(const QByteArray &output) } } } + +} // namespace Internal +} // namespace qbs diff --git a/src/app/qbs-setup-toolchains/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h index 2aced981d..d9856402d 100644 --- a/src/app/qbs-setup-toolchains/vsenvironmentdetector.h +++ b/src/lib/corelib/tools/vsenvironmentdetector.h @@ -31,15 +31,20 @@ #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 VsEnvironmentDetector +class QBS_EXPORT VsEnvironmentDetector { public: VsEnvironmentDetector(MSVC *msvc); @@ -56,4 +61,7 @@ private: QString m_errorString; }; +} // namespace Internal +} // namespace qbs + #endif // QBS_VSENVIRONMENTDETECTOR_H |