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