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