From 60a384cf2f3e2fcb9943f506787aadfa6eccd88c Mon Sep 17 00:00:00 2001 From: Bjoern Breitmeyer Date: Fri, 6 Feb 2015 11:10:39 +0100 Subject: Add QMake support for WEC2013 SDKs CESDKHandler now retrieves available WEC2013 SDKs from the registry and assembles a working build environment. Change-Id: Ifa70f53aca9d1bf2fadf178a331f46c1efca90ff Reviewed-by: Oswald Buddenhagen Reviewed-by: Joerg Bornemann --- qmake/generators/win32/cesdkhandler.cpp | 264 +++++++++++++++++++++++++++++++- qmake/generators/win32/cesdkhandler.h | 10 +- qmake/generators/win32/msvc_nmake.cpp | 2 +- 3 files changed, 267 insertions(+), 9 deletions(-) (limited to 'qmake') diff --git a/qmake/generators/win32/cesdkhandler.cpp b/qmake/generators/win32/cesdkhandler.cpp index 3afed2b8c1..1391cb297d 100644 --- a/qmake/generators/win32/cesdkhandler.cpp +++ b/qmake/generators/win32/cesdkhandler.cpp @@ -34,11 +34,22 @@ #include "cesdkhandler.h" #include +#include #include #include +#include +#include QT_BEGIN_NAMESPACE +struct PropertyContainer +{ + void clear() { name.clear(); value.clear(); properties.clear(); } + QString name; + QString value; + QMap properties; +}; + CeSdkInfo::CeSdkInfo() : m_major(0) , m_minor(0) { } @@ -47,22 +58,255 @@ CeSdkHandler::CeSdkHandler() { } -bool CeSdkHandler::parse() +struct ContainsPathKey +{ + bool operator()(const QString &val) + { + return !(val.endsWith(QStringLiteral("MSBuildToolsPath")) + || val.endsWith(QStringLiteral("MSBuildToolsRoot"))); + } +}; + +struct ValueFromKey +{ + ValueFromKey(const QSettings *settings) : settings(settings){} + QString operator()(const QString &key) + { + return settings->value(key).toString(); + } + + const QSettings *settings; +}; + +bool CeSdkHandler::parseMsBuildFile(QFile *file, CeSdkInfo *info) +{ + bool result = file->open(QFile::ReadOnly | QFile::Text); + const QString IncludePath = QStringLiteral("IncludePath"); + const QString LibraryPath = QStringLiteral("LibraryPath"); + const QString PreprocessorDefinitions = QStringLiteral("PreprocessorDefinitions"); + const QString SdkRootPathString = QStringLiteral("SdkRootPath"); + const QString ExecutablePath = QStringLiteral("ExecutablePath"); + enum ParserState{Not, Include, Lib, Define, BinDir, SdkRootPath}; + QString includePath; + QString libraryPath; + QString defines; + QString binDirs; + QString sdkRootPath; + ParserState state = Not; + if (result) { + QXmlStreamReader xml(file); + while (!xml.atEnd()) { + if (xml.isStartElement()) { + if (xml.name() == IncludePath) + state = Include; + else if (xml.name() == LibraryPath) + state = Lib; + else if (xml.name() == PreprocessorDefinitions) + state = Define; + else if (xml.name() == SdkRootPathString) + state = SdkRootPath; + else if (xml.name() == ExecutablePath) + state = BinDir; + else + state = Not; + } else if (xml.isEndElement()) { + state = Not; + } else if (xml.isCharacters()) { + switch (state) { + case Include: + includePath += xml.text(); + break; + case Lib: + libraryPath += xml.text(); + break; + case Define: + defines += xml.text(); + break; + case SdkRootPath: + sdkRootPath = xml.text().toString(); + break; + case BinDir: + binDirs += xml.text(); + case(Not): + break; + } + } + xml.readNext(); + } + } + file->close(); + const bool success = result && !includePath.isEmpty() && !libraryPath.isEmpty() && + !defines.isEmpty() && !sdkRootPath.isEmpty(); + if (success) { + const QString startPattern = QStringLiteral("$(Registry:"); + const int startIndex = sdkRootPath.indexOf(startPattern); + const int endIndex = sdkRootPath.lastIndexOf(QStringLiteral(")")); + const QString regString = sdkRootPath.mid(startIndex + startPattern.size(), + endIndex - startIndex - startPattern.size()); + QSettings sdkRootPathRegistry(regString, QSettings::NativeFormat); + const QString erg = sdkRootPathRegistry.value(QStringLiteral(".")).toString(); + const QString fullSdkRootPath = erg + sdkRootPath.mid(endIndex + 1); + const QString rootString = QStringLiteral("$(SdkRootPath)"); + + includePath = includePath.replace(rootString, fullSdkRootPath); + libraryPath = libraryPath.replace(rootString, fullSdkRootPath); + binDirs = binDirs.replace(rootString, fullSdkRootPath); + info->m_include = includePath + ";$(INCLUDE)"; + info->m_lib = libraryPath; + info->m_bin = binDirs; + } + return success; +} + +QStringList CeSdkHandler::getMsBuildToolPaths() const +{ + QSettings msbuildEntries("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\MSBuild\\ToolsVersions", + QSettings::NativeFormat); + const QStringList allKeys = msbuildEntries.allKeys(); + QStringList toolVersionKeys; + toolVersionKeys.push_back(QStringLiteral("c:\\Program Files\\MSBuild\\")); + std::remove_copy_if(allKeys.cbegin(), allKeys.cend(), + std::back_inserter(toolVersionKeys), ContainsPathKey()); + QStringList toolVersionValues; + std::transform(toolVersionKeys.constBegin(), toolVersionKeys.constEnd(), + std::back_inserter(toolVersionValues), + ValueFromKey(&msbuildEntries)); + return toolVersionValues; +} + +QStringList CeSdkHandler::filterMsBuildToolPaths(const QStringList &paths) const +{ + QStringList result; + foreach (const QString &path, paths) { + QDir dir(path); + if (path.endsWith(QStringLiteral("bin"))) + dir.cdUp(); + if (dir.cd(QStringLiteral("Microsoft.Cpp\\v4.0\\V110\\Platforms")) + || dir.cd(QStringLiteral("Microsoft.Cpp\\v4.0\\V120\\Platforms"))) { + result << dir.absolutePath(); + } + } + return result; +} + +bool CeSdkHandler::retrieveEnvironment(const QStringList &relativePaths, + const QStringList &toolPaths, + CeSdkInfo *info) +{ + bool result = false; + foreach (const QString &path, toolPaths) { + const QDir dir(path); + foreach (const QString &filePath, relativePaths) { + QFile file(dir.absoluteFilePath(filePath)); + if (file.exists()) + result = parseMsBuildFile(&file, info) || result; + } + } + + return result; +} + +void CeSdkHandler::retrieveWEC2013SDKs() +{ + const QStringList toolPaths = getMsBuildToolPaths(); + const QStringList filteredToolPaths = filterMsBuildToolPaths(toolPaths); + QSettings settings("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows CE Tools\\SDKs", QSettings::NativeFormat); + const QStringList keys = settings.allKeys(); + foreach (const QString &key, keys) { + if (key.contains(QLatin1String("SDKInformation")) || key.contains(QLatin1Char('.'))) { + QFile sdkPropertyFile(settings.value(key).toString()); + if (!sdkPropertyFile.exists()) + continue; + QFileInfo info(sdkPropertyFile); + if (info.isDir()) { + const QDir dir = info.absoluteFilePath(); + QFileInfo fInfo(dir.filePath(QLatin1String("Properties.xml"))); + if (fInfo.exists()) + sdkPropertyFile.setFileName(fInfo.absoluteFilePath()); + } + if (!sdkPropertyFile.open(QFile::ReadOnly)) + continue; + QXmlStreamReader xml(&sdkPropertyFile); + QString currentElement; + QString curName; + PropertyContainer currentProperty; + QVector propStack; + propStack.push_back(currentProperty); + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isStartElement()) { + currentElement = xml.name().toString(); + if (currentElement == QLatin1String("Property")) { + QXmlStreamAttributes attributes = xml.attributes(); + if (attributes.hasAttribute(QLatin1String("NAME"))) + curName = attributes.value(QLatin1String("NAME")).toString(); + Q_ASSERT(!curName.isEmpty()); + currentProperty.clear(); + currentProperty.name = curName; + propStack.push_back(currentProperty); + } else if (currentElement == QLatin1String("PropertyBag")) { + QXmlStreamAttributes attributes = xml.attributes(); + if (attributes.hasAttribute(QLatin1String("NAME"))) + curName = attributes.value(QLatin1String("NAME")).toString(); + Q_ASSERT(!curName.isEmpty()); + currentProperty.clear(); + currentProperty.name = curName; + propStack.push_back(currentProperty); + } + } else if (xml.isEndElement()) { + currentElement = xml.name().toString(); + PropertyContainer self = propStack.takeLast(); + if (currentElement != QLatin1String("Root")) { + PropertyContainer &last = propStack.last(); + last.properties[self.name] = self; + } else { + currentProperty = self; + } + } else if (xml.isCharacters()) { + PropertyContainer &self = propStack.last(); + self.value = xml.text().toString(); + } + } + + if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { + qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString(); + return; + } + CeSdkInfo currentSdk; + const PropertyContainer &cpuInfo = currentProperty.properties.value(QLatin1String("CPU info")); + if (cpuInfo.properties.isEmpty()) + continue; + const PropertyContainer &cpuInfoVal = cpuInfo.properties.first().properties.value(QLatin1String("CpuName")); + if (cpuInfoVal.name != QStringLiteral("CpuName")) + continue; + const QString SDKName = QStringLiteral("SDK name"); + currentSdk.m_name = currentProperty.properties.value(SDKName).value+ + QStringLiteral(" (") + cpuInfoVal.value + ")"; + currentSdk.m_major = currentProperty.properties.value(QLatin1String("OSMajor")).value.toInt(); + currentSdk.m_minor = currentProperty.properties.value(QLatin1String("OSMinor")).value.toInt(); + retrieveEnvironment(currentProperty.properties.value(QLatin1String("MSBuild Files110")).value.split(';'), + filteredToolPaths, ¤tSdk); + if (!currentSdk.m_include.isEmpty()) + m_list.append(currentSdk); + } + } +} + +void CeSdkHandler::retrieveWEC6n7SDKs() { // look at the file at %VCInstallDir%/vcpackages/WCE.VCPlatform.config // and scan through all installed sdks... - m_list.clear(); m_vcInstallDir = QString::fromLatin1(qgetenv("VCInstallDir")); if (m_vcInstallDir.isEmpty()) - return false; + return; QDir vStudioDir(m_vcInstallDir); if (!vStudioDir.cd(QLatin1String("vcpackages"))) - return false; + return; QFile configFile(vStudioDir.absoluteFilePath(QLatin1String("WCE.VCPlatform.config"))); if (!configFile.open(QIODevice::ReadOnly)) - return false; + return; QString currentElement; CeSdkInfo currentItem; @@ -94,10 +338,16 @@ bool CeSdkHandler::parse() if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) { qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString(); - return false; + return; } +} - return m_list.size() > 0; +bool CeSdkHandler::retrieveAvailableSDKs() +{ + m_list.clear(); + retrieveWEC2013SDKs(); + retrieveWEC6n7SDKs(); + return !m_list.empty(); } QString CeSdkHandler::fixPaths(QString path) const diff --git a/qmake/generators/win32/cesdkhandler.h b/qmake/generators/win32/cesdkhandler.h index 42c0121720..522712ff88 100644 --- a/qmake/generators/win32/cesdkhandler.h +++ b/qmake/generators/win32/cesdkhandler.h @@ -73,10 +73,18 @@ class CeSdkHandler { public: CeSdkHandler(); - bool parse(); + bool retrieveAvailableSDKs(); inline QList listAll() const { return m_list; } private: inline QString fixPaths(QString path) const; + void retrieveWEC6n7SDKs(); + void retrieveWEC2013SDKs(); + QStringList getMsBuildToolPaths() const; + QStringList filterMsBuildToolPaths(const QStringList &paths) const; + bool parseMsBuildFile(QFile *file, CeSdkInfo *info); + bool retrieveEnvironment(const QStringList &relativePaths, + const QStringList &toolPaths, + CeSdkInfo *info); QList m_list; QString m_vcInstallDir; }; diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp index daa356c8b0..eb8ae23384 100644 --- a/qmake/generators/win32/msvc_nmake.cpp +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -79,7 +79,7 @@ NmakeMakefileGenerator::writeMakefile(QTextStream &t) const ProValueMap &variables = project->variables(); if (project->isActiveConfig("wince")) { CeSdkHandler sdkhandler; - sdkhandler.parse(); + sdkhandler.retrieveAvailableSDKs(); const QString sdkName = variables["CE_SDK"].join(' ') + " (" + variables["CE_ARCH"].join(' ') + ")"; const QList sdkList = sdkhandler.listAll(); -- cgit v1.2.3