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