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