2013-04-25 16:02:17 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2014-01-07 13:27:11 +01:00
|
|
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
2013-04-25 16:02:17 +02:00
|
|
|
** 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
|
2014-10-01 13:21:18 +02:00
|
|
|
** conditions see http://www.qt.io/licensing. For further information
|
|
|
|
|
** use the contact form at http://www.qt.io/contact-us.
|
2013-04-25 16:02:17 +02:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2014-10-01 13:21:18 +02:00
|
|
|
** 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.
|
2013-04-25 16:02:17 +02:00
|
|
|
**
|
|
|
|
|
** 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"
|
2013-10-02 15:13:17 +02:00
|
|
|
|
2013-04-25 16:02:17 +02:00
|
|
|
#include <QDir>
|
2014-08-26 15:53:13 +02:00
|
|
|
#include <QFileInfo>
|
2013-04-25 16:02:17 +02:00
|
|
|
#include <QFileInfoList>
|
2014-08-26 15:53:13 +02:00
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QProcess>
|
|
|
|
|
|
|
|
|
|
static Q_LOGGING_CATEGORY(probeLog, "qtc.ios.probe")
|
2013-04-25 16:02:17 +02:00
|
|
|
|
|
|
|
|
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<QString, Platform> IosProbe::detectPlatforms(const QString &devPath)
|
|
|
|
|
{
|
|
|
|
|
IosProbe probe;
|
|
|
|
|
probe.addDeveloperPath(devPath);
|
2013-10-07 20:14:54 +02:00
|
|
|
probe.detectFirst();
|
2013-04-25 16:02:17 +02:00
|
|
|
return probe.detectedPlatforms();
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-02 15:13:17 +02:00
|
|
|
static int compareVersions(const QString &v1, const QString &v2)
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
|
|
|
|
QStringList v1L = v1.split(QLatin1Char('.'));
|
|
|
|
|
QStringList v2L = v2.split(QLatin1Char('.'));
|
|
|
|
|
int i = 0;
|
2014-08-04 21:19:59 +02:00
|
|
|
while (v1L.length() > i && v2L.length() > i) {
|
2013-04-25 16:02:17 +02:00
|
|
|
bool n1Ok, n2Ok;
|
|
|
|
|
int n1 = v1L.value(i).toInt(&n1Ok);
|
|
|
|
|
int n2 = v2L.value(i).toInt(&n2Ok);
|
|
|
|
|
if (!(n1Ok && n2Ok)) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(probeLog) << QString::fromLatin1("Failed to compare version %1 and %2").arg(v1, v2);
|
2013-04-25 16:02:17 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (n1 > n2)
|
|
|
|
|
return -1;
|
|
|
|
|
else if (n1 < n2)
|
|
|
|
|
return 1;
|
|
|
|
|
++i;
|
|
|
|
|
}
|
2014-08-04 21:19:59 +02:00
|
|
|
if (v1L.length() > v2L.length())
|
2013-04-25 16:02:17 +02:00
|
|
|
return -1;
|
2014-08-04 21:19:59 +02:00
|
|
|
if (v1L.length() < v2L.length())
|
2013-04-25 16:02:17 +02:00
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-02 15:13:17 +02:00
|
|
|
void IosProbe::addDeveloperPath(const QString &path)
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
|
|
|
|
if (path.isEmpty())
|
2013-10-02 15:13:17 +02:00
|
|
|
return;
|
2013-04-25 16:02:17 +02:00
|
|
|
QFileInfo pInfo(path);
|
|
|
|
|
if (!pInfo.exists() || !pInfo.isDir())
|
2013-10-02 15:13:17 +02:00
|
|
|
return;
|
2013-04-25 16:02:17 +02:00
|
|
|
if (m_developerPaths.contains(path))
|
2013-10-02 15:13:17 +02:00
|
|
|
return;
|
2013-04-25 16:02:17 +02:00
|
|
|
m_developerPaths.append(path);
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << QString::fromLatin1("Added developer path %1").arg(path);
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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()) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(probeLog) << QString::fromLatin1("Could not detect selected xcode with /usr/bin/xcode-select");
|
2013-04-25 16:02:17 +02:00
|
|
|
} else {
|
|
|
|
|
QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput());
|
2014-03-25 14:33:14 +01:00
|
|
|
path.chop(1);
|
2013-04-25 16:02:17 +02:00
|
|
|
addDeveloperPath(path);
|
|
|
|
|
}
|
|
|
|
|
addDeveloperPath(QLatin1String("/Applications/Xcode.app/Contents/Developer"));
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-02 15:13:17 +02:00
|
|
|
void IosProbe::setupDefaultToolchains(const QString &devPath, const QString &xcodeName)
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << QString::fromLatin1("Setting up platform \"%1\".").arg(xcodeName);
|
2013-04-25 16:02:17 +02:00
|
|
|
QString indent = QLatin1String(" ");
|
|
|
|
|
|
|
|
|
|
// detect clang (default toolchain)
|
|
|
|
|
QFileInfo clangFileInfo(devPath
|
|
|
|
|
+ QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin")
|
|
|
|
|
+ QLatin1String("/clang++"));
|
|
|
|
|
bool hasClang = clangFileInfo.exists();
|
2013-10-02 15:13:17 +02:00
|
|
|
if (!hasClang)
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(probeLog) << indent << QString::fromLatin1("Default toolchain %1 not found.")
|
|
|
|
|
.arg(clangFileInfo.canonicalFilePath());
|
2013-04-25 16:02:17 +02:00
|
|
|
// 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")) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << indent << QString::fromLatin1("Setting up %1").arg(fInfo.fileName());
|
2013-04-25 16:02:17 +02:00
|
|
|
QSettingsPtr infoSettings(new QSettings(
|
2013-10-02 15:13:17 +02:00
|
|
|
fInfo.absoluteFilePath() + QLatin1String("/Info.plist"),
|
|
|
|
|
QSettings::NativeFormat));
|
2013-04-25 16:02:17 +02:00
|
|
|
if (!infoSettings->contains(QLatin1String("Name"))) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(probeLog) << indent << QString::fromLatin1("Missing platform name in Info.plist of %1")
|
2013-04-25 16:02:17 +02:00
|
|
|
.arg(fInfo.absoluteFilePath());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
QString name = infoSettings->value(QLatin1String("Name")).toString();
|
|
|
|
|
if (name != QLatin1String("macosx") && name != QLatin1String("iphoneos")
|
|
|
|
|
&& name != QLatin1String("iphonesimulator"))
|
|
|
|
|
{
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(probeLog) << indent << QString::fromLatin1("Skipping unknown platform %1").arg(name);
|
2013-04-25 16:02:17 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// prepare default platform properties
|
|
|
|
|
QVariantMap defaultProp = infoSettings->value(QLatin1String("DefaultProperties"))
|
|
|
|
|
.toMap();
|
|
|
|
|
QVariantMap overrideProp = infoSettings->value(QLatin1String("OverrideProperties"))
|
|
|
|
|
.toMap();
|
|
|
|
|
QMapIterator<QString, QVariant> i(overrideProp);
|
|
|
|
|
while (i.hasNext()) {
|
|
|
|
|
i.next();
|
|
|
|
|
// use unite? might lead to double insertions...
|
|
|
|
|
defaultProp[i.key()] = i.value();
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-02 15:13:17 +02:00
|
|
|
QString clangFullName = name + QLatin1String("-clang") + xcodeName;
|
|
|
|
|
QString clang11FullName = name + QLatin1String("-clang11") + xcodeName;
|
2013-04-25 16:02:17 +02:00
|
|
|
// detect gcc
|
|
|
|
|
QFileInfo gccFileInfo(fInfo.absoluteFilePath() + QLatin1String("/Developer/usr/bin/g++"));
|
2013-10-02 15:13:17 +02:00
|
|
|
QString gccFullName = name + QLatin1String("-gcc") + xcodeName;
|
2013-04-25 16:02:17 +02:00
|
|
|
if (!gccFileInfo.exists())
|
|
|
|
|
gccFileInfo = QFileInfo(devPath + QLatin1String("/usr/bin/g++"));
|
|
|
|
|
bool hasGcc = gccFileInfo.exists();
|
|
|
|
|
|
|
|
|
|
QStringList extraFlags;
|
|
|
|
|
if (defaultProp.contains(QLatin1String("NATIVE_ARCH"))) {
|
|
|
|
|
QString arch = defaultProp.value(QLatin1String("NATIVE_ARCH")).toString();
|
|
|
|
|
if (!arch.startsWith(QLatin1String("arm")))
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(probeLog) << indent << QString::fromLatin1("Expected arm architecture, not %1").arg(arch);
|
2013-04-25 16:02:17 +02:00
|
|
|
extraFlags << QLatin1String("-arch") << arch;
|
2013-10-31 17:46:52 +01:00
|
|
|
} else if (name == QLatin1String("iphonesimulator")) {
|
|
|
|
|
// don't generate a toolchain for 64 bit (to fix when we support that)
|
|
|
|
|
extraFlags << QLatin1String("-arch") << QLatin1String("i386");
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
if (hasClang) {
|
|
|
|
|
Platform clangProfile;
|
2013-10-10 15:15:49 +02:00
|
|
|
clangProfile.developerPath = Utils::FileName::fromString(devPath);
|
2013-04-25 16:02:17 +02:00
|
|
|
clangProfile.platformKind = 0;
|
|
|
|
|
clangProfile.name = clangFullName;
|
|
|
|
|
clangProfile.platformPath = Utils::FileName(fInfo);
|
|
|
|
|
clangProfile.platformInfo = infoSettings;
|
|
|
|
|
clangProfile.compilerPath = Utils::FileName(clangFileInfo);
|
2013-10-31 17:46:52 +01:00
|
|
|
QStringList flags = extraFlags;
|
|
|
|
|
flags << QLatin1String("-dumpmachine");
|
|
|
|
|
QString compilerTriplet = qsystem(clangFileInfo.canonicalFilePath(), flags)
|
|
|
|
|
.simplified();
|
|
|
|
|
QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
|
|
|
|
|
clangProfile.architecture = compilerTripletl.value(0);
|
|
|
|
|
clangProfile.backendFlags = extraFlags;
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << indent << QString::fromLatin1("* adding profile %1").arg(clangProfile.name);
|
2013-10-02 15:13:17 +02:00
|
|
|
m_platforms[clangProfile.name] = clangProfile;
|
2013-04-25 16:02:17 +02:00
|
|
|
clangProfile.platformKind |= Platform::Cxx11Support;
|
|
|
|
|
clangProfile.backendFlags.append(QLatin1String("-std=c++11"));
|
|
|
|
|
clangProfile.backendFlags.append(QLatin1String("-stdlib=libc++"));
|
|
|
|
|
clangProfile.name = clang11FullName;
|
2013-10-02 15:13:17 +02:00
|
|
|
m_platforms[clangProfile.name] = clangProfile;
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
if (hasGcc) {
|
|
|
|
|
Platform gccProfile;
|
2013-10-10 15:15:49 +02:00
|
|
|
gccProfile.developerPath = Utils::FileName::fromString(devPath);
|
2013-04-25 16:02:17 +02:00
|
|
|
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);
|
2013-10-31 17:46:52 +01:00
|
|
|
QStringList flags = extraFlags;
|
|
|
|
|
flags << QLatin1String("-dumpmachine");
|
|
|
|
|
QString compilerTriplet = qsystem(gccFileInfo.canonicalFilePath(), flags)
|
|
|
|
|
.simplified();
|
|
|
|
|
QStringList compilerTripletl = compilerTriplet.split(QLatin1Char('-'));
|
|
|
|
|
gccProfile.architecture = compilerTripletl.value(0);
|
|
|
|
|
gccProfile.backendFlags = extraFlags;
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << indent << QString::fromLatin1("* adding profile %1").arg(gccProfile.name);
|
2013-10-02 15:13:17 +02:00
|
|
|
m_platforms[gccProfile.name] = gccProfile;
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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()
|
2013-10-02 15:13:17 +02:00
|
|
|
+ QLatin1String("/SDKSettings.plist"),
|
|
|
|
|
QSettings::NativeFormat));
|
2013-04-25 16:02:17 +02:00
|
|
|
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) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << indent << QString::fromLatin1("Skipping non base Sdk %1")
|
|
|
|
|
.arg(currentSdkName.toString());
|
2013-04-25 16:02:17 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (sdkName.isEmpty()) {
|
2014-08-25 11:30:09 +02:00
|
|
|
if (maxVersion.isEmpty() || compareVersions(maxVersion, versionStr) > 0) {
|
2013-04-25 16:02:17 +02:00
|
|
|
maxVersion = versionStr;
|
|
|
|
|
sdkPath = sdkDirInfo.canonicalFilePath();
|
|
|
|
|
sdkSettings = sdkInfo;
|
|
|
|
|
}
|
|
|
|
|
} else if (currentSdkName == sdkName) {
|
|
|
|
|
sdkPath = sdkDirInfo.canonicalFilePath();
|
|
|
|
|
sdkSettings = sdkInfo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!sdkPath.isEmpty())
|
|
|
|
|
sysRoot = sdkPath;
|
|
|
|
|
else if (!sdkName.isEmpty())
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(probeLog) << indent << QString::fromLatin1("Failed to find sysroot %1").arg(sdkName);
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
if (hasClang && !sysRoot.isEmpty()) {
|
2013-10-02 15:13:17 +02:00
|
|
|
m_platforms[clangFullName].platformKind |= Platform::BasePlatform;
|
|
|
|
|
m_platforms[clangFullName].sdkPath = Utils::FileName::fromString(sysRoot);
|
|
|
|
|
m_platforms[clangFullName].sdkSettings = sdkSettings;
|
|
|
|
|
m_platforms[clang11FullName].platformKind |= Platform::BasePlatform;
|
|
|
|
|
m_platforms[clang11FullName].sdkPath = Utils::FileName::fromString(sysRoot);
|
|
|
|
|
m_platforms[clang11FullName].sdkSettings = sdkSettings;
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
if (hasGcc && !sysRoot.isEmpty()) {
|
2013-10-02 15:13:17 +02:00
|
|
|
m_platforms[gccFullName].platformKind |= Platform::BasePlatform;
|
|
|
|
|
m_platforms[gccFullName].sdkPath = Utils::FileName::fromString(sysRoot);
|
|
|
|
|
m_platforms[gccFullName].sdkSettings = sdkSettings;
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
indent = QLatin1String(" ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-07 20:14:54 +02:00
|
|
|
void IosProbe::detectFirst()
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
|
|
|
|
detectDeveloperPaths();
|
2013-10-07 20:14:54 +02:00
|
|
|
if (!m_developerPaths.isEmpty())
|
|
|
|
|
setupDefaultToolchains(m_developerPaths.value(0),QLatin1String(""));
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QMap<QString, Platform> IosProbe::detectedPlatforms()
|
|
|
|
|
{
|
|
|
|
|
return m_platforms;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|