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