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