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 | |
9 | #include "clang/Basic/DarwinSDKInfo.h" |
10 | #include "llvm/Support/ErrorOr.h" |
11 | #include "llvm/Support/JSON.h" |
12 | #include "llvm/Support/MemoryBuffer.h" |
13 | #include "llvm/Support/Path.h" |
14 | #include <optional> |
15 | |
16 | using namespace clang; |
17 | |
18 | std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map( |
19 | const VersionTuple &Key, const VersionTuple &MinimumValue, |
20 | std::optional<VersionTuple> MaximumValue) const { |
21 | if (Key < MinimumKeyVersion) |
22 | return MinimumValue; |
23 | if (Key > MaximumKeyVersion) |
24 | return MaximumValue; |
25 | auto KV = Mapping.find(Val: Key.normalize()); |
26 | if (KV != Mapping.end()) |
27 | return KV->getSecond(); |
28 | // If no exact entry found, try just the major key version. Only do so when |
29 | // a minor version number is present, to avoid recursing indefinitely into |
30 | // the major-only check. |
31 | if (Key.getMinor()) |
32 | return map(Key: VersionTuple(Key.getMajor()), MinimumValue, MaximumValue); |
33 | // If this a major only key, return std::nullopt for a missing entry. |
34 | return std::nullopt; |
35 | } |
36 | |
37 | std::optional<DarwinSDKInfo::RelatedTargetVersionMapping> |
38 | DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON( |
39 | const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget) { |
40 | VersionTuple Min = VersionTuple(std::numeric_limits<unsigned>::max()); |
41 | VersionTuple Max = VersionTuple(0); |
42 | VersionTuple MinValue = Min; |
43 | llvm::DenseMap<VersionTuple, VersionTuple> Mapping; |
44 | for (const auto &KV : Obj) { |
45 | if (auto Val = KV.getSecond().getAsString()) { |
46 | llvm::VersionTuple KeyVersion; |
47 | llvm::VersionTuple ValueVersion; |
48 | if (KeyVersion.tryParse(string: KV.getFirst()) || ValueVersion.tryParse(string: *Val)) |
49 | return std::nullopt; |
50 | Mapping[KeyVersion.normalize()] = ValueVersion; |
51 | if (KeyVersion < Min) |
52 | Min = KeyVersion; |
53 | if (KeyVersion > Max) |
54 | Max = KeyVersion; |
55 | if (ValueVersion < MinValue) |
56 | MinValue = ValueVersion; |
57 | } |
58 | } |
59 | if (Mapping.empty()) |
60 | return std::nullopt; |
61 | return RelatedTargetVersionMapping( |
62 | Min, Max, MinValue, MaximumDeploymentTarget, std::move(Mapping)); |
63 | } |
64 | |
65 | static std::optional<VersionTuple> getVersionKey(const llvm::json::Object &Obj, |
66 | StringRef Key) { |
67 | auto Value = Obj.getString(K: Key); |
68 | if (!Value) |
69 | return std::nullopt; |
70 | VersionTuple Version; |
71 | if (Version.tryParse(string: *Value)) |
72 | return std::nullopt; |
73 | return Version; |
74 | } |
75 | |
76 | std::optional<DarwinSDKInfo> |
77 | DarwinSDKInfo::parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) { |
78 | auto Version = getVersionKey(Obj: *Obj, Key: "Version" ); |
79 | if (!Version) |
80 | return std::nullopt; |
81 | auto MaximumDeploymentVersion = |
82 | getVersionKey(Obj: *Obj, Key: "MaximumDeploymentTarget" ); |
83 | if (!MaximumDeploymentVersion) |
84 | return std::nullopt; |
85 | llvm::DenseMap<OSEnvPair::StorageType, |
86 | std::optional<RelatedTargetVersionMapping>> |
87 | VersionMappings; |
88 | if (const auto *VM = Obj->getObject(K: "VersionMap" )) { |
89 | // FIXME: Generalize this out beyond iOS-deriving targets. |
90 | // Look for ios_<targetos> version mapping for targets that derive from ios. |
91 | for (const auto &KV : *VM) { |
92 | auto Pair = StringRef(KV.getFirst()).split(Separator: "_" ); |
93 | if (Pair.first.compare_insensitive(RHS: "ios" ) == 0) { |
94 | llvm::Triple TT(llvm::Twine("--" ) + Pair.second.lower()); |
95 | if (TT.getOS() != llvm::Triple::UnknownOS) { |
96 | auto Mapping = RelatedTargetVersionMapping::parseJSON( |
97 | Obj: *KV.getSecond().getAsObject(), MaximumDeploymentTarget: *MaximumDeploymentVersion); |
98 | if (Mapping) |
99 | VersionMappings[OSEnvPair(llvm::Triple::IOS, |
100 | llvm::Triple::UnknownEnvironment, |
101 | TT.getOS(), |
102 | llvm::Triple::UnknownEnvironment) |
103 | .Value] = std::move(Mapping); |
104 | } |
105 | } |
106 | } |
107 | |
108 | if (const auto *Mapping = VM->getObject(K: "macOS_iOSMac" )) { |
109 | auto VersionMap = RelatedTargetVersionMapping::parseJSON( |
110 | Obj: *Mapping, MaximumDeploymentTarget: *MaximumDeploymentVersion); |
111 | if (!VersionMap) |
112 | return std::nullopt; |
113 | VersionMappings[OSEnvPair::macOStoMacCatalystPair().Value] = |
114 | std::move(VersionMap); |
115 | } |
116 | if (const auto *Mapping = VM->getObject(K: "iOSMac_macOS" )) { |
117 | auto VersionMap = RelatedTargetVersionMapping::parseJSON( |
118 | Obj: *Mapping, MaximumDeploymentTarget: *MaximumDeploymentVersion); |
119 | if (!VersionMap) |
120 | return std::nullopt; |
121 | VersionMappings[OSEnvPair::macCatalystToMacOSPair().Value] = |
122 | std::move(VersionMap); |
123 | } |
124 | } |
125 | |
126 | return DarwinSDKInfo(std::move(*Version), |
127 | std::move(*MaximumDeploymentVersion), |
128 | std::move(VersionMappings)); |
129 | } |
130 | |
131 | Expected<std::optional<DarwinSDKInfo>> |
132 | clang::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { |
133 | llvm::SmallString<256> Filepath = SDKRootPath; |
134 | llvm::sys::path::append(path&: Filepath, a: "SDKSettings.json" ); |
135 | llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File = |
136 | VFS.getBufferForFile(Name: Filepath); |
137 | if (!File) { |
138 | // If the file couldn't be read, assume it just doesn't exist. |
139 | return std::nullopt; |
140 | } |
141 | Expected<llvm::json::Value> Result = |
142 | llvm::json::parse(JSON: File.get()->getBuffer()); |
143 | if (!Result) |
144 | return Result.takeError(); |
145 | |
146 | if (const auto *Obj = Result->getAsObject()) { |
147 | if (auto SDKInfo = DarwinSDKInfo::parseDarwinSDKSettingsJSON(Obj)) |
148 | return std::move(SDKInfo); |
149 | } |
150 | return llvm::make_error<llvm::StringError>(Args: "invalid SDKSettings.json" , |
151 | Args: llvm::inconvertibleErrorCode()); |
152 | } |
153 | |