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