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 
17 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>
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 
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>
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>>
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