clang 23.0.0git
DarwinSDKInfo.cpp
Go to the documentation of this file.
1//===--- DarwinSDKInfo.cpp - SDK Information parser for darwin - ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "llvm/ADT/StringSwitch.h"
11#include "llvm/Support/ErrorOr.h"
12#include "llvm/Support/JSON.h"
13#include "llvm/Support/MemoryBuffer.h"
14#include "llvm/Support/Path.h"
15#include "llvm/TargetParser/ARMTargetParser.h"
16#include <optional>
17
18using namespace clang;
19
21 const VersionTuple &Key, const VersionTuple &MinimumValue,
22 std::optional<VersionTuple> MaximumValue) const {
23 if (Key < MinimumKeyVersion)
24 return MinimumValue;
25 if (Key > MaximumKeyVersion)
26 return MaximumValue;
27 auto KV = Mapping.find(Key.normalize());
28 if (KV != Mapping.end())
29 return KV->getSecond();
30 // If no exact entry found, try just the major key version. Only do so when
31 // a minor version number is present, to avoid recursing indefinitely into
32 // the major-only check.
33 if (Key.getMinor())
34 return map(VersionTuple(Key.getMajor()), MinimumValue, MaximumValue);
35 // If this a major only key, return std::nullopt for a missing entry.
36 return std::nullopt;
37}
38
39std::optional<DarwinSDKInfo::RelatedTargetVersionMapping>
41 const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget) {
42 VersionTuple Min = VersionTuple(std::numeric_limits<unsigned>::max());
43 VersionTuple Max = VersionTuple(0);
44 VersionTuple MinValue = Min;
45 llvm::DenseMap<VersionTuple, VersionTuple> Mapping;
46 for (const auto &KV : Obj) {
47 if (auto Val = KV.getSecond().getAsString()) {
48 llvm::VersionTuple KeyVersion;
49 llvm::VersionTuple ValueVersion;
50 if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val))
51 return std::nullopt;
52 Mapping[KeyVersion.normalize()] = ValueVersion;
53 if (KeyVersion < Min)
54 Min = KeyVersion;
55 if (KeyVersion > Max)
56 Max = KeyVersion;
57 if (ValueVersion < MinValue)
58 MinValue = ValueVersion;
59 }
60 }
61 if (Mapping.empty())
62 return std::nullopt;
64 Min, Max, MinValue, MaximumDeploymentTarget, std::move(Mapping));
65}
66
67static std::optional<StringRef>
68parseXcodePlatform(const llvm::json::Object &Obj) {
69 // The CanonicalName is the Xcode platform followed by a version, e.g.
70 // macosx15.0.
71 auto CanonicalName = Obj.getString("CanonicalName");
72 if (!CanonicalName)
73 return std::nullopt;
74 size_t VersionStart = CanonicalName->find_first_of("0123456789");
75 return CanonicalName->slice(0, VersionStart);
76}
77
78static std::pair<llvm::Triple::OSType, llvm::Triple::EnvironmentType>
79parseOSAndEnvironment(std::optional<StringRef> XcodePlatform) {
80 if (!XcodePlatform)
81 return {llvm::Triple::UnknownOS, llvm::Triple::UnknownEnvironment};
82
83 llvm::Triple::OSType OS =
84 llvm::StringSwitch<llvm::Triple::OSType>(*XcodePlatform)
85 .Case("macosx", llvm::Triple::MacOSX)
86 .Cases({"iphoneos", "iphonesimulator"}, llvm::Triple::IOS)
87 .Cases({"appletvos", "appletvsimulator"}, llvm::Triple::TvOS)
88 .Cases({"watchos", "watchsimulator"}, llvm::Triple::WatchOS)
89 .Case("bridgeos", llvm::Triple::BridgeOS)
90 .Cases({"xros", "xrsimulator"}, llvm::Triple::XROS)
91 .Case("driverkit", llvm::Triple::DriverKit)
92 .Default(llvm::Triple::UnknownOS);
93
94 llvm::Triple::EnvironmentType Environment =
95 llvm::StringSwitch<llvm::Triple::EnvironmentType>(*XcodePlatform)
96 .Cases({"iphonesimulator", "appletvsimulator", "watchsimulator",
97 "xrsimulator"},
98 llvm::Triple::Simulator)
99 .Default(llvm::Triple::UnknownEnvironment);
100
101 return {OS, Environment};
102}
103
105legacyPlatformInfos(llvm::Triple::OSType SDKOS,
106 llvm::Triple::EnvironmentType SDKEnvironment) {
108 // Synthesize platform infos for older SDKs from the first SDKs with
109 // SupportedTargets: macOS 10.15 (DriverKit 19.0), iOS 13.0, tvOS 13.0,
110 // watchOS 6.0. Older macOS SDKs supported ppc and i386 architectures, and
111 // maybe ppc64. Support for those is difficult to identify from the SDK, and
112 // so aren't listed here. Other older SDKs (especially iOS) most likely
113 // supported armv6 and armv7 architectures that aren't listed here either.
114 switch (SDKOS) {
115 case llvm::Triple::MacOSX:
116 PlatformInfos.push_back({{llvm::Triple("x86_64-apple-macosx")}, ""});
117 // macOS 10.15 also has a Mac Catalyst supported target, but Mac Catalyst
118 // was new in that version so omit it for older versions.
119 break;
120 case llvm::Triple::DriverKit:
121 // DriverKit 19.0 only had SDKSettings.plist, which isn't used. So this code
122 // path is used in 19.x as well, not just earlier versions.
123 PlatformInfos.push_back(
124 {{llvm::Triple("x86_64-apple-driverkit")}, "/System/DriverKit"});
125 break;
126 case llvm::Triple::IOS:
127 switch (SDKEnvironment) {
128 case llvm::Triple::UnknownEnvironment:
129 PlatformInfos.push_back(
130 {{llvm::Triple("armv7-apple-ios"), llvm::Triple("armv7s-apple-ios"),
131 llvm::Triple("arm64-apple-ios")},
132 ""});
133 break;
134 case llvm::Triple::Simulator:
135 PlatformInfos.push_back(
136 {{llvm::Triple("x86_64-apple-ios-simulator")}, ""});
137 break;
138 default:
139 break;
140 }
141 break;
142 case llvm::Triple::TvOS:
143 switch (SDKEnvironment) {
144 case llvm::Triple::UnknownEnvironment:
145 PlatformInfos.push_back({{llvm::Triple("arm64-apple-tvos")}, ""});
146 break;
147 case llvm::Triple::Simulator:
148 PlatformInfos.push_back(
149 {{llvm::Triple("x86_64-apple-tvos-simulator")}, ""});
150 break;
151 default:
152 break;
153 }
154 break;
155 case llvm::Triple::WatchOS:
156 switch (SDKEnvironment) {
157 case llvm::Triple::UnknownEnvironment:
158 PlatformInfos.push_back({{llvm::Triple("armv7k-apple-watchos"),
159 llvm::Triple("arm64_32-apple-watchos")},
160 ""});
161 break;
162 case llvm::Triple::Simulator:
163 PlatformInfos.push_back(
164 {{llvm::Triple("x86_64-apple-watchos-simulator")}, ""});
165 break;
166 default:
167 break;
168 }
169 break;
170 case llvm::Triple::BridgeOS:
171 PlatformInfos.push_back({{llvm::Triple("armv7-apple-bridgeos"),
172 llvm::Triple("armv7s-apple-bridgeos"),
173 llvm::Triple("arm64-apple-bridgeos")},
174 ""});
175 break;
176 default:
177 break;
178 }
179 return PlatformInfos;
180}
181
183 const llvm::json::Object &Obj, std::optional<StringRef> XcodePlatform,
184 llvm::Triple::OSType SDKOS, llvm::Triple::EnvironmentType SDKEnvironment,
185 VersionTuple Version) {
187 auto SupportedTargets = Obj.getObject("SupportedTargets");
188 if (!SupportedTargets)
189 return legacyPlatformInfos(SDKOS, SDKEnvironment);
190
191 for (const auto &SupportedTargetPair : *SupportedTargets) {
192 const llvm::json::Object *SupportedTarget =
193 SupportedTargetPair.getSecond().getAsObject();
194 if (!SupportedTarget)
195 continue;
196
197 auto Archs = SupportedTarget->getArray("Archs");
198 auto Vendor = SupportedTarget->getString("LLVMTargetTripleVendor");
199 auto OS = SupportedTarget->getString("LLVMTargetTripleSys");
200 if (!Archs || !Vendor || !OS)
201 continue;
202
204 auto Environment =
205 SupportedTarget->getString("LLVMTargetTripleEnvironment");
206 for (const auto &ArchValue : *Archs) {
207 if (auto Arch = ArchValue.getAsString()) {
208 if (Environment && !Environment->empty())
209 Triples.emplace_back(*Arch, *Vendor, *OS, *Environment);
210 else
211 Triples.emplace_back(*Arch, *Vendor, *OS);
212 }
213 }
214 if (Triples.empty())
215 continue;
216
217 // The key is either the Xcode platform, or a variant. The platform must be
218 // the first entry in the returned PlatformInfoStorageType.
219 StringRef PlatformOrVariant = SupportedTargetPair.getFirst();
220
221 StringRef EffectivePlatformPrefix;
222 // Ignore iosmac value if it exists.
223 if ((PlatformOrVariant != "iosmac") || (Version >= VersionTuple(99))) {
224 auto PlatformPrefix = SupportedTarget->getString("SystemPrefix");
225 if (PlatformPrefix) {
226 EffectivePlatformPrefix = *PlatformPrefix;
227 } else {
228 // Older SDKs don't have SystemPrefix in SupportedTargets, manually add
229 // their prefixes.
230 if ((Triples[0].getOS() == llvm::Triple::DriverKit) &&
231 (Version < VersionTuple(22, 1)))
232 EffectivePlatformPrefix = "/System/DriverKit";
233 }
234 }
235
236 if (PlatformOrVariant == XcodePlatform)
237 PlatformInfos.insert(PlatformInfos.begin(),
238 {std::move(Triples), EffectivePlatformPrefix});
239 else
240 PlatformInfos.emplace_back(std::move(Triples), EffectivePlatformPrefix);
241 }
242 return PlatformInfos;
243}
244
245static std::optional<VersionTuple> getVersionKey(const llvm::json::Object &Obj,
246 StringRef Key) {
247 auto Value = Obj.getString(Key);
248 if (!Value)
249 return std::nullopt;
250 VersionTuple Version;
251 if (Version.tryParse(*Value))
252 return std::nullopt;
253 return Version;
254}
255
256std::optional<DarwinSDKInfo>
258 const llvm::json::Object *Obj) {
259 auto Version = getVersionKey(*Obj, "Version");
260 if (!Version)
261 return std::nullopt;
262 auto MaximumDeploymentVersion =
263 getVersionKey(*Obj, "MaximumDeploymentTarget");
264 if (!MaximumDeploymentVersion)
265 return std::nullopt;
266 std::optional<StringRef> XcodePlatform = parseXcodePlatform(*Obj);
267 std::pair<llvm::Triple::OSType, llvm::Triple::EnvironmentType>
268 OSAndEnvironment = parseOSAndEnvironment(XcodePlatform);
269 // DisplayName should always be present, but don't require it.
270 StringRef DisplayName =
271 Obj->getString("DisplayName")
272 .value_or(Obj->getString("CanonicalName").value_or("<unknown>"));
273 PlatformInfoStorageType PlatformInfos =
274 parsePlatformInfos(*Obj, XcodePlatform, OSAndEnvironment.first,
275 OSAndEnvironment.second, *Version);
276 if (PlatformInfos.empty())
277 return std::nullopt;
278 llvm::DenseMap<OSEnvPair::StorageType,
279 std::optional<RelatedTargetVersionMapping>>
280 VersionMappings;
281 if (const auto *VM = Obj->getObject("VersionMap")) {
282 // FIXME: Generalize this out beyond iOS-deriving targets.
283 // Look for ios_<targetos> version mapping for targets that derive from ios.
284 for (const auto &KV : *VM) {
285 auto Pair = StringRef(KV.getFirst()).split("_");
286 if (Pair.first.compare_insensitive("ios") == 0) {
287 llvm::Triple TT(llvm::Twine("--") + Pair.second.lower());
288 if (TT.getOS() != llvm::Triple::UnknownOS) {
290 *KV.getSecond().getAsObject(), *MaximumDeploymentVersion);
291 if (Mapping)
292 VersionMappings[OSEnvPair(llvm::Triple::IOS,
293 llvm::Triple::UnknownEnvironment,
294 TT.getOS(),
295 llvm::Triple::UnknownEnvironment)
296 .Value] = std::move(Mapping);
297 }
298 }
299 }
300
301 if (const auto *Mapping = VM->getObject("macOS_iOSMac")) {
303 *Mapping, *MaximumDeploymentVersion);
304 if (!VersionMap)
305 return std::nullopt;
306 VersionMappings[OSEnvPair::macOStoMacCatalystPair().Value] =
307 std::move(VersionMap);
308 }
309 if (const auto *Mapping = VM->getObject("iOSMac_macOS")) {
311 *Mapping, *MaximumDeploymentVersion);
312 if (!VersionMap)
313 return std::nullopt;
314 VersionMappings[OSEnvPair::macCatalystToMacOSPair().Value] =
315 std::move(VersionMap);
316 }
317 }
318
319 return DarwinSDKInfo(std::move(FilePath), OSAndEnvironment.first,
320 OSAndEnvironment.second, std::move(*Version),
321 DisplayName, std::move(*MaximumDeploymentVersion),
322 std::move(PlatformInfos), std::move(VersionMappings));
323}
324
326clang::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) {
327 llvm::SmallString<256> Filepath = SDKRootPath;
328 llvm::sys::path::append(Filepath, "SDKSettings.json");
329 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
330 VFS.getBufferForFile(Filepath);
331 if (!File) {
332 // If the file couldn't be read, assume it just doesn't exist.
333 return std::nullopt;
334 }
336 llvm::json::parse(File.get()->getBuffer());
337 if (!Result)
338 return Result.takeError();
339
340 if (const auto *Obj = Result->getAsObject()) {
342 Filepath.str().str(), Obj))
343 return std::move(SDKInfo);
344 }
345 return llvm::make_error<llvm::StringError>("invalid SDKSettings.json",
346 llvm::inconvertibleErrorCode());
347}
348
349DarwinSDKInfo::DarwinSDKInfo(llvm::Triple::OSType OS,
350 llvm::Triple::EnvironmentType Environment,
351 VersionTuple Version, StringRef DisplayName,
352 VersionTuple MaximumDeploymentTarget)
353 : DarwinSDKInfo("", OS, Environment, Version, DisplayName,
354 MaximumDeploymentTarget,
355 legacyPlatformInfos(OS, Environment)) {}
356
357static DarwinSDKInfo::PlatformInfoStorageType::const_iterator
359 const llvm::Triple &Triple) {
360 auto PlatformInfoIt = llvm::find_if(
361 PlatformInfos,
362 [&Triple](const DarwinSDKInfo::SDKPlatformInfo &PlatformInfo) {
363 const auto &Triples = PlatformInfo.getTriples();
364 return llvm::find(Triples, Triple) != Triples.end();
365 });
366
367 // The SDK specifies values for Xcode to use for the -target argument. It's
368 // hard to perfectly match the triple passed to this function against those
369 // values though. The passed in triple might have been computed from just
370 // -arch, or it might have been modified by -march and several other arguments
371 // that can effect any of the triple components. It's not really possible to
372 // account for all of the triple variations, but one common modification is
373 // that "arm" gets changed to "thumb". If the passed in triple is "thumb", try
374 // mapping it back to an "arm" triple since that's what the SDK will specify.
375 if (PlatformInfoIt == PlatformInfos.end() &&
376 Triple.getArch() == llvm::Triple::thumb) {
377 StringRef ARMArch = llvm::Triple::getArchName(llvm::Triple::arm);
378
379 // Preserve the sub-arch from the triple.
380 llvm::ARM::ArchKind ArchKind = llvm::ARM::parseArch(Triple.getArchName());
381 StringRef SubArch = llvm::ARM::getSubArch(ArchKind);
382
383 llvm::Triple ARMTriple(Triple);
384 ARMTriple.setArchName((ARMArch + SubArch).str());
385 if (ARMTriple.getArch() != llvm::Triple::thumb) {
386 // Sometimes changing the architecture name in the triple doesn't change
387 // its parsed architecture. e.g. armv6m parses as thumb, so it won't help
388 // to search for armv6m if thumbv6m already failed.
389 PlatformInfoIt = findPlatformInfo(PlatformInfos, ARMTriple);
390 }
391 }
392
393 return PlatformInfoIt;
394}
395
396bool DarwinSDKInfo::supportsTriple(const llvm::Triple &Triple) const {
397 return findPlatformInfo(PlatformInfos, Triple) != PlatformInfos.end();
398}
399
400StringRef DarwinSDKInfo::getPlatformPrefix(const llvm::Triple &Triple) const {
401 auto PlatformInfoIt = findPlatformInfo(PlatformInfos, Triple);
402 if (PlatformInfoIt != PlatformInfos.end())
403 return PlatformInfoIt->getPlatformPrefix();
404
405 // This triple probably isn't supported by the SDK. However, almost every SDK
406 // just has a single prefix where all of its contents are, so return that.
407 StringRef PlatformPrefix = PlatformInfos[0].getPlatformPrefix();
408 for (PlatformInfoIt = std::next(PlatformInfos.begin());
409 PlatformInfoIt != PlatformInfos.end(); ++PlatformInfoIt) {
410 if (PlatformInfoIt->getPlatformPrefix() != PlatformPrefix) {
411 // This SDK has multiple prefixes, give up and return nothing.
412 return StringRef();
413 }
414 }
415 return PlatformPrefix;
416}
static DarwinSDKInfo::PlatformInfoStorageType::const_iterator findPlatformInfo(const DarwinSDKInfo::PlatformInfoStorageType &PlatformInfos, const llvm::Triple &Triple)
static DarwinSDKInfo::PlatformInfoStorageType legacyPlatformInfos(llvm::Triple::OSType SDKOS, llvm::Triple::EnvironmentType SDKEnvironment)
static std::optional< StringRef > parseXcodePlatform(const llvm::json::Object &Obj)
static DarwinSDKInfo::PlatformInfoStorageType parsePlatformInfos(const llvm::json::Object &Obj, std::optional< StringRef > XcodePlatform, llvm::Triple::OSType SDKOS, llvm::Triple::EnvironmentType SDKEnvironment, VersionTuple Version)
static std::pair< llvm::Triple::OSType, llvm::Triple::EnvironmentType > parseOSAndEnvironment(std::optional< StringRef > XcodePlatform)
static std::optional< VersionTuple > getVersionKey(const llvm::json::Object &Obj, StringRef Key)
std::optional< VersionTuple > map(const VersionTuple &Key, const VersionTuple &MinimumValue, std::optional< VersionTuple > MaximumValue) const
Returns the mapped key, or the appropriate Minimum / MaximumValue if they key is outside of the mappi...
RelatedTargetVersionMapping(VersionTuple MinimumKeyVersion, VersionTuple MaximumKeyVersion, VersionTuple MinimumValue, VersionTuple MaximumValue, llvm::DenseMap< VersionTuple, VersionTuple > Mapping)
static std::optional< RelatedTargetVersionMapping > parseJSON(const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget)
static std::optional< DarwinSDKInfo > parseDarwinSDKSettingsJSON(std::string FilePath, const llvm::json::Object *Obj)
SmallVector< SDKPlatformInfo, 2 > PlatformInfoStorageType
DarwinSDKInfo(std::string FilePath, llvm::Triple::OSType OS, llvm::Triple::EnvironmentType Environment, VersionTuple Version, StringRef DisplayName, VersionTuple MaximumDeploymentTarget, PlatformInfoStorageType PlatformInfos, llvm::DenseMap< OSEnvPair::StorageType, std::optional< RelatedTargetVersionMapping > > VersionMappings=llvm::DenseMap< OSEnvPair::StorageType, std::optional< RelatedTargetVersionMapping > >())
llvm::Triple::OSType getOS() const
StringRef getPlatformPrefix(const llvm::Triple &Triple) const
bool supportsTriple(const llvm::Triple &Triple) const
The JSON file list parser is used to communicate input to InstallAPI.
Expected< std::optional< DarwinSDKInfo > > parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath)
Parse the SDK information from the SDKSettings.json file.
@ Default
Set to the current date and time.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
A value that describes two os-environment pairs that can be used as a key to the version map in the S...
static constexpr OSEnvPair macCatalystToMacOSPair()
Returns the os-environment mapping pair that's used to represent the Mac Catalyst -> macOS version ma...
static constexpr OSEnvPair macOStoMacCatalystPair()
Returns the os-environment mapping pair that's used to represent the macOS -> Mac Catalyst version ma...
Information about the supported platforms, derived from the target triple definitions,...
const TripleStorageType & getTriples() const
SmallVector< llvm::Triple, 5 > TripleStorageType