105a6d74cSAlex Lorenz //===--- DarwinSDKInfo.cpp - SDK Information parser for darwin - ----------===//
205a6d74cSAlex Lorenz //
305a6d74cSAlex Lorenz // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
405a6d74cSAlex Lorenz // See https://llvm.org/LICENSE.txt for license information.
505a6d74cSAlex Lorenz // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
605a6d74cSAlex Lorenz //
705a6d74cSAlex Lorenz //===----------------------------------------------------------------------===//
805a6d74cSAlex Lorenz
905a6d74cSAlex Lorenz #include "clang/Basic/DarwinSDKInfo.h"
1005a6d74cSAlex Lorenz #include "llvm/Support/ErrorOr.h"
1105a6d74cSAlex Lorenz #include "llvm/Support/JSON.h"
1205a6d74cSAlex Lorenz #include "llvm/Support/MemoryBuffer.h"
1305a6d74cSAlex Lorenz #include "llvm/Support/Path.h"
14a12b82adSKazu Hirata #include <optional>
1505a6d74cSAlex Lorenz
1605a6d74cSAlex Lorenz using namespace clang;
1705a6d74cSAlex Lorenz
map(const VersionTuple & Key,const VersionTuple & MinimumValue,std::optional<VersionTuple> MaximumValue) const182c5d49cfSFangrui Song std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map(
19808bbc2cSAlex Lorenz const VersionTuple &Key, const VersionTuple &MinimumValue,
202c5d49cfSFangrui Song std::optional<VersionTuple> MaximumValue) const {
21808bbc2cSAlex Lorenz if (Key < MinimumKeyVersion)
22808bbc2cSAlex Lorenz return MinimumValue;
23808bbc2cSAlex Lorenz if (Key > MaximumKeyVersion)
24808bbc2cSAlex Lorenz return MaximumValue;
25808bbc2cSAlex Lorenz auto KV = Mapping.find(Key.normalize());
26808bbc2cSAlex Lorenz if (KV != Mapping.end())
27808bbc2cSAlex Lorenz return KV->getSecond();
28808bbc2cSAlex Lorenz // If no exact entry found, try just the major key version. Only do so when
29808bbc2cSAlex Lorenz // a minor version number is present, to avoid recursing indefinitely into
30808bbc2cSAlex Lorenz // the major-only check.
31808bbc2cSAlex Lorenz if (Key.getMinor())
32808bbc2cSAlex Lorenz return map(VersionTuple(Key.getMajor()), MinimumValue, MaximumValue);
3335b4fbb5SKazu Hirata // If this a major only key, return std::nullopt for a missing entry.
34eeee3feeSKazu Hirata return std::nullopt;
35808bbc2cSAlex Lorenz }
36808bbc2cSAlex Lorenz
37*6ad0788cSKazu Hirata std::optional<DarwinSDKInfo::RelatedTargetVersionMapping>
parseJSON(const llvm::json::Object & Obj,VersionTuple MaximumDeploymentTarget)38808bbc2cSAlex Lorenz DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON(
39808bbc2cSAlex Lorenz const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget) {
40808bbc2cSAlex Lorenz VersionTuple Min = VersionTuple(std::numeric_limits<unsigned>::max());
41808bbc2cSAlex Lorenz VersionTuple Max = VersionTuple(0);
42808bbc2cSAlex Lorenz VersionTuple MinValue = Min;
43808bbc2cSAlex Lorenz llvm::DenseMap<VersionTuple, VersionTuple> Mapping;
44808bbc2cSAlex Lorenz for (const auto &KV : Obj) {
45808bbc2cSAlex Lorenz if (auto Val = KV.getSecond().getAsString()) {
46808bbc2cSAlex Lorenz llvm::VersionTuple KeyVersion;
47808bbc2cSAlex Lorenz llvm::VersionTuple ValueVersion;
48808bbc2cSAlex Lorenz if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val))
49eeee3feeSKazu Hirata return std::nullopt;
50808bbc2cSAlex Lorenz Mapping[KeyVersion.normalize()] = ValueVersion;
51808bbc2cSAlex Lorenz if (KeyVersion < Min)
52808bbc2cSAlex Lorenz Min = KeyVersion;
53808bbc2cSAlex Lorenz if (KeyVersion > Max)
54808bbc2cSAlex Lorenz Max = KeyVersion;
55808bbc2cSAlex Lorenz if (ValueVersion < MinValue)
56808bbc2cSAlex Lorenz MinValue = ValueVersion;
57808bbc2cSAlex Lorenz }
58808bbc2cSAlex Lorenz }
59808bbc2cSAlex Lorenz if (Mapping.empty())
60eeee3feeSKazu Hirata return std::nullopt;
61808bbc2cSAlex Lorenz return RelatedTargetVersionMapping(
62808bbc2cSAlex Lorenz Min, Max, MinValue, MaximumDeploymentTarget, std::move(Mapping));
63808bbc2cSAlex Lorenz }
64808bbc2cSAlex Lorenz
getVersionKey(const llvm::json::Object & Obj,StringRef Key)65a12b82adSKazu Hirata static std::optional<VersionTuple> getVersionKey(const llvm::json::Object &Obj,
66808bbc2cSAlex Lorenz StringRef Key) {
67808bbc2cSAlex Lorenz auto Value = Obj.getString(Key);
68808bbc2cSAlex Lorenz if (!Value)
69eeee3feeSKazu Hirata return std::nullopt;
70808bbc2cSAlex Lorenz VersionTuple Version;
71808bbc2cSAlex Lorenz if (Version.tryParse(*Value))
72eeee3feeSKazu Hirata return std::nullopt;
73808bbc2cSAlex Lorenz return Version;
74808bbc2cSAlex Lorenz }
75808bbc2cSAlex Lorenz
762c5d49cfSFangrui Song std::optional<DarwinSDKInfo>
parseDarwinSDKSettingsJSON(const llvm::json::Object * Obj)77808bbc2cSAlex Lorenz DarwinSDKInfo::parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) {
78808bbc2cSAlex Lorenz auto Version = getVersionKey(*Obj, "Version");
79808bbc2cSAlex Lorenz if (!Version)
80eeee3feeSKazu Hirata return std::nullopt;
81808bbc2cSAlex Lorenz auto MaximumDeploymentVersion =
82808bbc2cSAlex Lorenz getVersionKey(*Obj, "MaximumDeploymentTarget");
83808bbc2cSAlex Lorenz if (!MaximumDeploymentVersion)
84eeee3feeSKazu Hirata return std::nullopt;
85*6ad0788cSKazu Hirata llvm::DenseMap<OSEnvPair::StorageType,
86*6ad0788cSKazu Hirata std::optional<RelatedTargetVersionMapping>>
87808bbc2cSAlex Lorenz VersionMappings;
88808bbc2cSAlex Lorenz if (const auto *VM = Obj->getObject("VersionMap")) {
89809c6a5aSEgor Zhdan // FIXME: Generalize this out beyond iOS-deriving targets.
90809c6a5aSEgor Zhdan // Look for ios_<targetos> version mapping for targets that derive from ios.
91809c6a5aSEgor Zhdan for (const auto &KV : *VM) {
92809c6a5aSEgor Zhdan auto Pair = StringRef(KV.getFirst()).split("_");
93809c6a5aSEgor Zhdan if (Pair.first.compare_insensitive("ios") == 0) {
94809c6a5aSEgor Zhdan llvm::Triple TT(llvm::Twine("--") + Pair.second.lower());
95809c6a5aSEgor Zhdan if (TT.getOS() != llvm::Triple::UnknownOS) {
96809c6a5aSEgor Zhdan auto Mapping = RelatedTargetVersionMapping::parseJSON(
97809c6a5aSEgor Zhdan *KV.getSecond().getAsObject(), *MaximumDeploymentVersion);
98809c6a5aSEgor Zhdan if (Mapping)
99809c6a5aSEgor Zhdan VersionMappings[OSEnvPair(llvm::Triple::IOS,
100809c6a5aSEgor Zhdan llvm::Triple::UnknownEnvironment,
101809c6a5aSEgor Zhdan TT.getOS(),
102809c6a5aSEgor Zhdan llvm::Triple::UnknownEnvironment)
103809c6a5aSEgor Zhdan .Value] = std::move(Mapping);
104809c6a5aSEgor Zhdan }
105809c6a5aSEgor Zhdan }
106809c6a5aSEgor Zhdan }
107809c6a5aSEgor Zhdan
108808bbc2cSAlex Lorenz if (const auto *Mapping = VM->getObject("macOS_iOSMac")) {
109808bbc2cSAlex Lorenz auto VersionMap = RelatedTargetVersionMapping::parseJSON(
110808bbc2cSAlex Lorenz *Mapping, *MaximumDeploymentVersion);
111808bbc2cSAlex Lorenz if (!VersionMap)
112eeee3feeSKazu Hirata return std::nullopt;
113808bbc2cSAlex Lorenz VersionMappings[OSEnvPair::macOStoMacCatalystPair().Value] =
114808bbc2cSAlex Lorenz std::move(VersionMap);
115808bbc2cSAlex Lorenz }
1162542c1a5SAlex Lorenz if (const auto *Mapping = VM->getObject("iOSMac_macOS")) {
1172542c1a5SAlex Lorenz auto VersionMap = RelatedTargetVersionMapping::parseJSON(
1182542c1a5SAlex Lorenz *Mapping, *MaximumDeploymentVersion);
1192542c1a5SAlex Lorenz if (!VersionMap)
120eeee3feeSKazu Hirata return std::nullopt;
1212542c1a5SAlex Lorenz VersionMappings[OSEnvPair::macCatalystToMacOSPair().Value] =
1222542c1a5SAlex Lorenz std::move(VersionMap);
1232542c1a5SAlex Lorenz }
124808bbc2cSAlex Lorenz }
125808bbc2cSAlex Lorenz
126808bbc2cSAlex Lorenz return DarwinSDKInfo(std::move(*Version),
127808bbc2cSAlex Lorenz std::move(*MaximumDeploymentVersion),
128808bbc2cSAlex Lorenz std::move(VersionMappings));
129808bbc2cSAlex Lorenz }
130808bbc2cSAlex Lorenz
1312c5d49cfSFangrui Song Expected<std::optional<DarwinSDKInfo>>
parseDarwinSDKInfo(llvm::vfs::FileSystem & VFS,StringRef SDKRootPath)13205a6d74cSAlex Lorenz clang::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) {
13305a6d74cSAlex Lorenz llvm::SmallString<256> Filepath = SDKRootPath;
13405a6d74cSAlex Lorenz llvm::sys::path::append(Filepath, "SDKSettings.json");
13505a6d74cSAlex Lorenz llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
13605a6d74cSAlex Lorenz VFS.getBufferForFile(Filepath);
13705a6d74cSAlex Lorenz if (!File) {
13805a6d74cSAlex Lorenz // If the file couldn't be read, assume it just doesn't exist.
139eeee3feeSKazu Hirata return std::nullopt;
14005a6d74cSAlex Lorenz }
14105a6d74cSAlex Lorenz Expected<llvm::json::Value> Result =
14205a6d74cSAlex Lorenz llvm::json::parse(File.get()->getBuffer());
14305a6d74cSAlex Lorenz if (!Result)
14405a6d74cSAlex Lorenz return Result.takeError();
14505a6d74cSAlex Lorenz
14605a6d74cSAlex Lorenz if (const auto *Obj = Result->getAsObject()) {
147eb26ba9dSAlex Lorenz if (auto SDKInfo = DarwinSDKInfo::parseDarwinSDKSettingsJSON(Obj))
148eb26ba9dSAlex Lorenz return std::move(SDKInfo);
14905a6d74cSAlex Lorenz }
15005a6d74cSAlex Lorenz return llvm::make_error<llvm::StringError>("invalid SDKSettings.json",
15105a6d74cSAlex Lorenz llvm::inconvertibleErrorCode());
15205a6d74cSAlex Lorenz }
153