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