177 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Basic/DarwinSDKInfo.h"10#include "llvm/ADT/StringSwitch.h"11#include "llvm/Support/ErrorOr.h"12#include "llvm/Support/JSON.h"13#include "llvm/Support/MemoryBuffer.h"14#include "llvm/Support/Path.h"15#include <optional>16 17using namespace clang;18 19std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map(20 const VersionTuple &Key, const VersionTuple &MinimumValue,21 std::optional<VersionTuple> MaximumValue) const {22 if (Key < MinimumKeyVersion)23 return MinimumValue;24 if (Key > MaximumKeyVersion)25 return MaximumValue;26 auto KV = Mapping.find(Key.normalize());27 if (KV != Mapping.end())28 return KV->getSecond();29 // If no exact entry found, try just the major key version. Only do so when30 // a minor version number is present, to avoid recursing indefinitely into31 // the major-only check.32 if (Key.getMinor())33 return map(VersionTuple(Key.getMajor()), MinimumValue, MaximumValue);34 // If this a major only key, return std::nullopt for a missing entry.35 return std::nullopt;36}37 38std::optional<DarwinSDKInfo::RelatedTargetVersionMapping>39DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON(40 const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget) {41 VersionTuple Min = VersionTuple(std::numeric_limits<unsigned>::max());42 VersionTuple Max = VersionTuple(0);43 VersionTuple MinValue = Min;44 llvm::DenseMap<VersionTuple, VersionTuple> Mapping;45 for (const auto &KV : Obj) {46 if (auto Val = KV.getSecond().getAsString()) {47 llvm::VersionTuple KeyVersion;48 llvm::VersionTuple ValueVersion;49 if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val))50 return std::nullopt;51 Mapping[KeyVersion.normalize()] = ValueVersion;52 if (KeyVersion < Min)53 Min = KeyVersion;54 if (KeyVersion > Max)55 Max = KeyVersion;56 if (ValueVersion < MinValue)57 MinValue = ValueVersion;58 }59 }60 if (Mapping.empty())61 return std::nullopt;62 return RelatedTargetVersionMapping(63 Min, Max, MinValue, MaximumDeploymentTarget, std::move(Mapping));64}65 66static llvm::Triple::OSType parseOS(const llvm::json::Object &Obj) {67 // The CanonicalName is the Xcode platform followed by a version, e.g.68 // macosx16.0.69 auto CanonicalName = Obj.getString("CanonicalName");70 if (!CanonicalName)71 return llvm::Triple::UnknownOS;72 size_t VersionStart = CanonicalName->find_first_of("0123456789");73 StringRef XcodePlatform = CanonicalName->slice(0, VersionStart);74 return llvm::StringSwitch<llvm::Triple::OSType>(XcodePlatform)75 .Case("macosx", llvm::Triple::MacOSX)76 .Case("iphoneos", llvm::Triple::IOS)77 .Case("iphonesimulator", llvm::Triple::IOS)78 .Case("appletvos", llvm::Triple::TvOS)79 .Case("appletvsimulator", llvm::Triple::TvOS)80 .Case("watchos", llvm::Triple::WatchOS)81 .Case("watchsimulator", llvm::Triple::WatchOS)82 .Case("xros", llvm::Triple::XROS)83 .Case("xrsimulator", llvm::Triple::XROS)84 .Case("driverkit", llvm::Triple::DriverKit)85 .Default(llvm::Triple::UnknownOS);86}87 88static std::optional<VersionTuple> getVersionKey(const llvm::json::Object &Obj,89 StringRef Key) {90 auto Value = Obj.getString(Key);91 if (!Value)92 return std::nullopt;93 VersionTuple Version;94 if (Version.tryParse(*Value))95 return std::nullopt;96 return Version;97}98 99std::optional<DarwinSDKInfo>100DarwinSDKInfo::parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) {101 auto Version = getVersionKey(*Obj, "Version");102 if (!Version)103 return std::nullopt;104 auto MaximumDeploymentVersion =105 getVersionKey(*Obj, "MaximumDeploymentTarget");106 if (!MaximumDeploymentVersion)107 return std::nullopt;108 llvm::Triple::OSType OS = parseOS(*Obj);109 llvm::DenseMap<OSEnvPair::StorageType,110 std::optional<RelatedTargetVersionMapping>>111 VersionMappings;112 if (const auto *VM = Obj->getObject("VersionMap")) {113 // FIXME: Generalize this out beyond iOS-deriving targets.114 // Look for ios_<targetos> version mapping for targets that derive from ios.115 for (const auto &KV : *VM) {116 auto Pair = StringRef(KV.getFirst()).split("_");117 if (Pair.first.compare_insensitive("ios") == 0) {118 llvm::Triple TT(llvm::Twine("--") + Pair.second.lower());119 if (TT.getOS() != llvm::Triple::UnknownOS) {120 auto Mapping = RelatedTargetVersionMapping::parseJSON(121 *KV.getSecond().getAsObject(), *MaximumDeploymentVersion);122 if (Mapping)123 VersionMappings[OSEnvPair(llvm::Triple::IOS,124 llvm::Triple::UnknownEnvironment,125 TT.getOS(),126 llvm::Triple::UnknownEnvironment)127 .Value] = std::move(Mapping);128 }129 }130 }131 132 if (const auto *Mapping = VM->getObject("macOS_iOSMac")) {133 auto VersionMap = RelatedTargetVersionMapping::parseJSON(134 *Mapping, *MaximumDeploymentVersion);135 if (!VersionMap)136 return std::nullopt;137 VersionMappings[OSEnvPair::macOStoMacCatalystPair().Value] =138 std::move(VersionMap);139 }140 if (const auto *Mapping = VM->getObject("iOSMac_macOS")) {141 auto VersionMap = RelatedTargetVersionMapping::parseJSON(142 *Mapping, *MaximumDeploymentVersion);143 if (!VersionMap)144 return std::nullopt;145 VersionMappings[OSEnvPair::macCatalystToMacOSPair().Value] =146 std::move(VersionMap);147 }148 }149 150 return DarwinSDKInfo(std::move(*Version),151 std::move(*MaximumDeploymentVersion), OS,152 std::move(VersionMappings));153}154 155Expected<std::optional<DarwinSDKInfo>>156clang::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) {157 llvm::SmallString<256> Filepath = SDKRootPath;158 llvm::sys::path::append(Filepath, "SDKSettings.json");159 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =160 VFS.getBufferForFile(Filepath);161 if (!File) {162 // If the file couldn't be read, assume it just doesn't exist.163 return std::nullopt;164 }165 Expected<llvm::json::Value> Result =166 llvm::json::parse(File.get()->getBuffer());167 if (!Result)168 return Result.takeError();169 170 if (const auto *Obj = Result->getAsObject()) {171 if (auto SDKInfo = DarwinSDKInfo::parseDarwinSDKSettingsJSON(Obj))172 return std::move(SDKInfo);173 }174 return llvm::make_error<llvm::StringError>("invalid SDKSettings.json",175 llvm::inconvertibleErrorCode());176}177