1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "iosprobe.h"
27
28 #include <utils/algorithm.h>
29 #include <utils/qtcprocess.h>
30
31 #include <QDir>
32 #include <QFileInfo>
33 #include <QFileInfoList>
34 #include <QLoggingCategory>
35 #include <QProcess>
36
37 static Q_LOGGING_CATEGORY(probeLog, "qtc.ios.probe", QtWarningMsg)
38
39 using namespace Utils;
40
41 namespace Ios {
42
43 static QString defaultDeveloperPath = QLatin1String("/Applications/Xcode.app/Contents/Developer");
44
detectPlatforms(const QString & devPath)45 QMap<QString, XcodePlatform> XcodeProbe::detectPlatforms(const QString &devPath)
46 {
47 XcodeProbe probe;
48 probe.addDeveloperPath(devPath);
49 probe.detectFirst();
50 return probe.detectedPlatforms();
51 }
52
addDeveloperPath(const QString & path)53 void XcodeProbe::addDeveloperPath(const QString &path)
54 {
55 if (path.isEmpty())
56 return;
57 QFileInfo pInfo(path);
58 if (!pInfo.exists() || !pInfo.isDir())
59 return;
60 if (m_developerPaths.contains(path))
61 return;
62 m_developerPaths.append(path);
63 qCDebug(probeLog) << QString::fromLatin1("Added developer path %1").arg(path);
64 }
65
detectDeveloperPaths()66 void XcodeProbe::detectDeveloperPaths()
67 {
68 Utils::QtcProcess selectedXcode;
69 selectedXcode.setTimeoutS(5);
70 selectedXcode.setCommand({"/usr/bin/xcode-select", {"--print-path"}});
71 selectedXcode.runBlocking();
72 if (selectedXcode.result() != QtcProcess::FinishedWithSuccess)
73 qCWarning(probeLog)
74 << QString::fromLatin1("Could not detect selected Xcode using xcode-select");
75 else
76 addDeveloperPath(selectedXcode.stdOut().trimmed());
77 addDeveloperPath(defaultDeveloperPath);
78 }
79
setupDefaultToolchains(const QString & devPath)80 void XcodeProbe::setupDefaultToolchains(const QString &devPath)
81 {
82 auto getClangInfo = [devPath](const QString &compiler) {
83 QFileInfo compilerInfo(devPath
84 + QLatin1String("/Toolchains/XcodeDefault.xctoolchain/usr/bin/")
85 + compiler);
86 if (!compilerInfo.exists())
87 qCWarning(probeLog) << QString::fromLatin1("Default toolchain %1 not found.")
88 .arg(compilerInfo.canonicalFilePath());
89 return compilerInfo;
90 };
91
92 XcodePlatform clangProfile;
93 clangProfile.developerPath = Utils::FilePath::fromString(devPath);
94
95 const QFileInfo clangCInfo = getClangInfo("clang");
96 if (clangCInfo.exists())
97 clangProfile.cCompilerPath = Utils::FilePath::fromFileInfo(clangCInfo);
98
99 const QFileInfo clangCppInfo = getClangInfo("clang++");
100 if (clangCppInfo.exists())
101 clangProfile.cxxCompilerPath = Utils::FilePath::fromFileInfo(clangCppInfo);
102
103 QSet<QString> allArchitectures;
104 static const std::map<QString, QStringList> sdkConfigs {
105 {QLatin1String("AppleTVOS"), QStringList("arm64")},
106 {QLatin1String("AppleTVSimulator"), QStringList("x86_64")},
107 {QLatin1String("iPhoneOS"), QStringList { QLatin1String("arm64"), QLatin1String("armv7") }},
108 {QLatin1String("iPhoneSimulator"), QStringList { QLatin1String("x86_64"),
109 QLatin1String("i386") }},
110 {QLatin1String("MacOSX"), QStringList { QLatin1String("x86_64"), QLatin1String("i386") }},
111 {QLatin1String("WatchOS"), QStringList("armv7k")},
112 {QLatin1String("WatchSimulator"), QStringList("i386")}
113 };
114 for (const auto &sdkConfig : sdkConfigs) {
115 XcodePlatform::SDK sdk;
116 sdk.directoryName = sdkConfig.first;
117 sdk.path = Utils::FilePath::fromString(devPath
118 + QString(QLatin1String("/Platforms/%1.platform/Developer/SDKs/%1.sdk")).arg(
119 sdk.directoryName));
120 sdk.architectures = sdkConfig.second;
121 const QFileInfo sdkPathInfo(sdk.path.toString());
122 if (sdkPathInfo.exists() && sdkPathInfo.isDir()) {
123 clangProfile.sdks.push_back(sdk);
124 allArchitectures += Utils::toSet(sdk.architectures);
125 }
126 }
127
128 if (!clangProfile.cCompilerPath.isEmpty() || !clangProfile.cxxCompilerPath.isEmpty()) {
129 for (const QString &arch : qAsConst(allArchitectures)) {
130 const QString clangFullName = QString(QLatin1String("Apple Clang (%1)")).arg(arch)
131 + ((devPath != defaultDeveloperPath)
132 ? QString(QLatin1String(" in %1")).arg(devPath)
133 : QString());
134
135 XcodePlatform::ToolchainTarget target;
136 target.name = clangFullName;
137 target.architecture = arch;
138 target.backendFlags = QStringList { QLatin1String("-arch"), arch };
139 clangProfile.targets.push_back(target);
140 }
141 }
142
143 m_platforms[devPath] = clangProfile;
144 }
145
detectFirst()146 void XcodeProbe::detectFirst()
147 {
148 detectDeveloperPaths();
149 if (!m_developerPaths.isEmpty())
150 setupDefaultToolchains(m_developerPaths.first());
151 }
152
detectedPlatforms()153 QMap<QString, XcodePlatform> XcodeProbe::detectedPlatforms()
154 {
155 return m_platforms;
156 }
157
operator ==(const XcodePlatform & other) const158 bool XcodePlatform::operator==(const XcodePlatform &other) const
159 {
160 return developerPath == other.developerPath;
161 }
162
qHash(const XcodePlatform & platform)163 uint qHash(const XcodePlatform &platform)
164 {
165 return qHash(platform.developerPath);
166 }
167
qHash(const XcodePlatform::ToolchainTarget & target)168 uint qHash(const XcodePlatform::ToolchainTarget &target)
169 {
170 return qHash(target.name);
171 }
172
operator ==(const XcodePlatform::ToolchainTarget & other) const173 bool XcodePlatform::ToolchainTarget::operator==(const XcodePlatform::ToolchainTarget &other) const
174 {
175 return architecture == other.architecture;
176 }
177
178 }
179