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