From 8d96ce557ef5b49d0cfea2b1633e09354948088c Mon Sep 17 00:00:00 2001 From: Fawzi Mohamed Date: Thu, 25 Apr 2013 16:02:17 +0200 Subject: ios: preliminary support for ios first work in progress support for ios * separate iosTool using xml communication used for device info and run * iossim tool to handle the simulator * debug prepared but not working * separate gcc toolchain detection fix for simulator 1) add a QT built for ios 2) open a project, for example qtbase/examples/widgets/animation/animatedtiles/animatedtiles.pro 3) build/run... Change-Id: I7e01604e416338cbe4692dfb34f5d3f31312702d Reviewed-by: Eike Ziller --- src/plugins/ios/iosprobe.cpp | 355 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 src/plugins/ios/iosprobe.cpp (limited to 'src/plugins/ios/iosprobe.cpp') diff --git a/src/plugins/ios/iosprobe.cpp b/src/plugins/ios/iosprobe.cpp new file mode 100644 index 0000000000..318220cb8d --- /dev/null +++ b/src/plugins/ios/iosprobe.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "iosprobe.h" +#include +#include +#include +#include +#include + +static const bool debugProbe = false; + +namespace Ios { + +static QString qsystem(const QString &exe, const QStringList &args = QStringList()) +{ + QProcess p; + p.setProcessChannelMode(QProcess::MergedChannels); + p.start(exe, args); + p.waitForFinished(); + return QString::fromLocal8Bit(p.readAll()); +} + +QMap IosProbe::detectPlatforms(const QString &devPath) +{ + IosProbe probe; + probe.addDeveloperPath(devPath); + probe.detectAll(); + return probe.detectedPlatforms(); +} + +int IosProbe::compareVersions(const QString &v1, const QString &v2) +{ + QStringList v1L = v1.split(QLatin1Char('.')); + QStringList v2L = v2.split(QLatin1Char('.')); + int i = 0; + while (v1.length() > i && v1.length() > i) { + bool n1Ok, n2Ok; + int n1 = v1L.value(i).toInt(&n1Ok); + int n2 = v2L.value(i).toInt(&n2Ok); + if (!(n1Ok && n2Ok)) { + qDebug() << QString::fromLatin1("Failed to compare version %1 and %2").arg(v1,v2); + return 0; + } + if (n1 > n2) + return -1; + else if (n1 < n2) + return 1; + ++i; + } + if (v1.length() > v2.length()) + return -1; + if (v1.length() < v2.length()) + return 1; + return 0; +} + +bool IosProbe::addDeveloperPath(const QString &path) +{ + if (path.isEmpty()) + return false; + QFileInfo pInfo(path); + if (!pInfo.exists() || !pInfo.isDir()) + return false; + if (m_developerPaths.contains(path)) + return false; + m_developerPaths.append(path); + if (debugProbe) + qDebug() << QString::fromLatin1("Added developer path %1").arg(path); + return true; +} + +void IosProbe::detectDeveloperPaths() +{ + QProcess selectedXcode; + QString program = QLatin1String("/usr/bin/xcode-select"); + QStringList arguments(QLatin1String("--print-path")); + selectedXcode.start(program, arguments, QProcess::ReadOnly); + if (!selectedXcode.waitForFinished() || selectedXcode.exitCode()) { + qDebug() << QString::fromLatin1("Could not detect selected xcode with /usr/bin/xcode-select"); + } else { + QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput()); + addDeveloperPath(path); + } + addDeveloperPath(QLatin1String("/Applications/Xcode.app/Contents/Developer")); +} + +void IosProbe::setArch(Platform *platform, const QString &pathToGcc, const QStringList &extraFlags) +{ + if (!extraFlags.isEmpty()) + platform->backendFlags = extraFlags; + // setting architecture and endianness only here, bercause the same compiler + // can support several ones + QStringList flags(extraFlags); + flags << QLatin1String("-dumpmachine"); + QString compilerTriplet = qsystem(pathToGcc, flags).simplified(); + QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-')); + if (compilerTripletl.count() < 2) { + qDebug() << QString::fromLatin1("Detected '%1', but I don't understand " + "its architecture '%2'.") + .arg(pathToGcc, compilerTriplet); + return; + } + + QString endianness, architecture; + architecture = compilerTripletl.at(0); + endianness = QLatin1String("little"); + + if (debugProbe) + qDebug() << QString::fromLatin1(" Toolchain %1 detected:\n" + " binary: %2\n" + " triplet: %3\n" + " arch: %4").arg(platform->name, pathToGcc, + compilerTriplet, architecture); + + platform->architecture = architecture; +} + +void IosProbe::setupDefaultToolchains(const QString &devPath, const QString &xCodeName) +{ + if (debugProbe) + qDebug() << QString::fromLatin1("Setting up platform '%1'.").arg(xCodeName); + QString indent = QLatin1String(" "); + + // detect clang (default toolchain) + QFileInfo clangFileInfo(devPath + + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin") + + QLatin1String("/clang++")); + bool hasClang = clangFileInfo.exists(); + QSettingsPtr toolchainInfo; + if (hasClang) + toolchainInfo = QSettingsPtr(new QSettings( + devPath + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/ToolchainInfo.plist") + , QSettings::NativeFormat)); + else + qDebug() << indent << QString::fromLatin1("Default toolchain %1 not found.") + .arg(clangFileInfo.canonicalFilePath()); + // Platforms + QDir platformsDir(devPath + QLatin1String("/Platforms")); + QFileInfoList platforms = platformsDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + foreach (const QFileInfo &fInfo, platforms) { + if (fInfo.isDir() && fInfo.suffix() == QLatin1String("platform")) { + if (debugProbe) + qDebug() << indent << QString::fromLatin1("Setting up %1").arg(fInfo.fileName()); + QSettingsPtr infoSettings(new QSettings( + fInfo.absoluteFilePath() + QLatin1String("/Info.plist") + , QSettings::NativeFormat)); + if (!infoSettings->contains(QLatin1String("Name"))) { + qDebug() << indent << QString::fromLatin1("Missing platform name in Info.plist of %1") + .arg(fInfo.absoluteFilePath()); + continue; + } + QString targetOS; + QString name = infoSettings->value(QLatin1String("Name")).toString(); + if (name != QLatin1String("macosx") && name != QLatin1String("iphoneos") + && name != QLatin1String("iphonesimulator")) + { + qDebug() << indent << QString::fromLatin1("Skipping unknown platform %1").arg(name); + continue; + } + + // prepare default platform properties + QVariantMap defaultProp = infoSettings->value(QLatin1String("DefaultProperties")) + .toMap(); + QVariantMap overrideProp = infoSettings->value(QLatin1String("OverrideProperties")) + .toMap(); + QMapIterator i(overrideProp); + while (i.hasNext()) { + i.next(); + // use unite? might lead to double insertions... + defaultProp[i.key()] = i.value(); + } + + QString clangFullName = name + QLatin1String("-clang") + xCodeName; + QString clang11FullName = name + QLatin1String("-clang11") + xCodeName; + // detect gcc + QFileInfo gccFileInfo(fInfo.absoluteFilePath() + QLatin1String("/Developer/usr/bin/g++")); + QString gccFullName = name + QLatin1String("-gcc") + xCodeName; + if (!gccFileInfo.exists()) + gccFileInfo = QFileInfo(devPath + QLatin1String("/usr/bin/g++")); + bool hasGcc = gccFileInfo.exists(); + + QStringList extraFlags; + if (defaultProp.contains(QLatin1String("ARCHS"))) { + QString arch = defaultProp.value(QLatin1String("ARCHS")).toString(); + if (arch == QLatin1String("$(NATIVE_ARCH_32_BIT)")) + extraFlags << QLatin1String("-arch") << QLatin1String("i386"); + } + if (defaultProp.contains(QLatin1String("NATIVE_ARCH"))) { + QString arch = defaultProp.value(QLatin1String("NATIVE_ARCH")).toString(); + if (!arch.startsWith(QLatin1String("arm"))) + qDebug() << indent << QString::fromLatin1("Expected arm architecture, not %1").arg(arch); + extraFlags << QLatin1String("-arch") << arch; + } + if (hasClang) { + Platform clangProfile; + clangProfile.platformKind = 0; + clangProfile.name = clangFullName; + clangProfile.platformPath = Utils::FileName(fInfo); + clangProfile.platformInfo = infoSettings; + clangProfile.compilerPath = Utils::FileName(clangFileInfo); + setArch(&clangProfile, clangFileInfo.canonicalFilePath(), extraFlags); + if (debugProbe) + qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(clangProfile.name); + this->m_platforms[clangProfile.name] = clangProfile; + clangProfile.platformKind |= Platform::Cxx11Support; + clangProfile.backendFlags.append(QLatin1String("-std=c++11")); + clangProfile.backendFlags.append(QLatin1String("-stdlib=libc++")); + clangProfile.name = clang11FullName; + this->m_platforms[clangProfile.name] = clangProfile; + } + if (hasGcc) { + Platform gccProfile; + gccProfile.name = gccFullName; + gccProfile.platformKind = 0; + // use the arm-apple-darwin10-llvm-* variant and avoid the extraFlags if available??? + gccProfile.platformPath = Utils::FileName(fInfo); + gccProfile.platformInfo = infoSettings; + gccProfile.compilerPath = Utils::FileName(gccFileInfo); + setArch(&gccProfile, gccFileInfo.canonicalFilePath(), extraFlags); + if (debugProbe) + qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(gccProfile.name); + this->m_platforms[gccProfile.name] = gccProfile; + } + + // set SDKs/sysroot + QString sysRoot; + QSettingsPtr sdkSettings; + { + QString sdkName; + if (defaultProp.contains(QLatin1String("SDKROOT"))) + sdkName = defaultProp.value(QLatin1String("SDKROOT")).toString(); + QString sdkPath; + QDir sdks(fInfo.absoluteFilePath() + QLatin1String("/Developer/SDKs")); + QString maxVersion; + foreach (const QFileInfo &sdkDirInfo, sdks.entryInfoList(QDir::Dirs + | QDir::NoDotAndDotDot)) { + indent = QLatin1String(" "); + QSettingsPtr sdkInfo(new QSettings(sdkDirInfo.absoluteFilePath() + + QLatin1String("/SDKSettings.plist") + , QSettings::NativeFormat)); + QString versionStr = sdkInfo->value(QLatin1String("Version")).toString(); + QVariant currentSdkName = sdkInfo->value(QLatin1String("CanonicalName")); + bool isBaseSdk = sdkInfo->value((QLatin1String("isBaseSDK"))).toString() + .toLower() != QLatin1String("no"); + if (!isBaseSdk) { + if (debugProbe) + qDebug() << indent << QString::fromLatin1("Skipping non base Sdk %1") + .arg(currentSdkName.toString()); + continue; + } + QString safeName = currentSdkName.toString().replace(QLatin1Char('-'), QLatin1Char('_')) + .replace(QRegExp(QLatin1String("[^-a-zA-Z0-9]")),QLatin1String("-")); + if (sdkName.isEmpty()) { + if (compareVersions(maxVersion,versionStr) > 0) { + maxVersion = versionStr; + sdkPath = sdkDirInfo.canonicalFilePath(); + sdkSettings = sdkInfo; + } + } else if (currentSdkName == sdkName) { + sdkPath = sdkDirInfo.canonicalFilePath(); + sdkSettings = sdkInfo; + } + if (hasClang){ + Platform pSdk; + pSdk = this->m_platforms[clangFullName]; + pSdk.name = safeName + QLatin1String("-clang") + xCodeName; + pSdk.sdkPath = Utils::FileName(sdkDirInfo); + pSdk.sdkSettings = sdkInfo; + if (debugProbe) + qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(pSdk.name); + this->m_platforms[pSdk.name] = pSdk; + pSdk.backendFlags.append(QLatin1String("-std=c++11")); + pSdk.backendFlags.append(QLatin1String("-stdlib=libc++")); + pSdk.name = safeName + QLatin1String("-clang11") + xCodeName; + this->m_platforms[pSdk.name] = pSdk; + } + if (hasGcc) { + Platform pSdk; + pSdk = this->m_platforms[gccFullName]; + pSdk.name = safeName + QLatin1String("-gcc") + xCodeName; + pSdk.sdkPath = Utils::FileName(sdkDirInfo); + pSdk.sdkSettings = sdkInfo; + if (debugProbe) + qDebug() << indent << QString::fromLatin1("* adding profile %1").arg(pSdk.name); + this->m_platforms[pSdk.name] = pSdk; + } + } + if (!sdkPath.isEmpty()) + sysRoot = sdkPath; + else if (!sdkName.isEmpty()) + qDebug() << indent << QString::fromLatin1("Failed to find sysroot %1").arg(sdkName); + } + //this->m_platforms.remove(clangFullName); + if (hasClang && !sysRoot.isEmpty()) { + this->m_platforms[clangFullName].platformKind |= Platform::BasePlatform; + this->m_platforms[clangFullName].sdkPath = Utils::FileName::fromString(sysRoot); + this->m_platforms[clangFullName].sdkSettings = sdkSettings; + this->m_platforms[clang11FullName].platformKind |= Platform::BasePlatform; + this->m_platforms[clang11FullName].sdkPath = Utils::FileName::fromString(sysRoot); + this->m_platforms[clang11FullName].sdkSettings = sdkSettings; + } + //this->m_platforms.remove(gccFullName); + if (hasGcc && !sysRoot.isEmpty()) { + this->m_platforms[gccFullName].platformKind |= Platform::BasePlatform; + this->m_platforms[gccFullName].sdkPath = Utils::FileName::fromString(sysRoot); + this->m_platforms[gccFullName].sdkSettings = sdkSettings; + } + } + indent = QLatin1String(" "); + } +} + +void IosProbe::detectAll() +{ + detectDeveloperPaths(); + QString xcodeName = QLatin1String(""); + for (int iXcode = 0; iXcode < m_developerPaths.count(); ++iXcode) { + setupDefaultToolchains(m_developerPaths.value(iXcode), xcodeName); + xcodeName = QString::fromLatin1("-%1").arg(iXcode + 2); + } +} + +QMap IosProbe::detectedPlatforms() +{ + return m_platforms; +} + +} -- cgit v1.2.1