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