1//===--- DarwinSDKInfo.h - SDK Information parser for darwin ----*- C++ -*-===//
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#ifndef LLVM_CLANG_BASIC_DARWINSDKINFO_H
10#define LLVM_CLANG_BASIC_DARWINSDKINFO_H
11
12#include "clang/Basic/LLVM.h"
13#include "llvm/ADT/DenseMap.h"
14#include "llvm/ADT/Triple.h"
15#include "llvm/Support/Error.h"
16#include "llvm/Support/VersionTuple.h"
17#include "llvm/Support/VirtualFileSystem.h"
18#include <optional>
19
20namespace llvm {
21namespace json {
22class Object;
23} // end namespace json
24} // end namespace llvm
25
26namespace clang {
27
28/// The information about the darwin SDK that was used during this compilation.
29class DarwinSDKInfo {
30public:
31  /// A value that describes two os-environment pairs that can be used as a key
32  /// to the version map in the SDK.
33  struct OSEnvPair {
34  public:
35    using StorageType = uint64_t;
36
37    constexpr OSEnvPair(llvm::Triple::OSType FromOS,
38                        llvm::Triple::EnvironmentType FromEnv,
39                        llvm::Triple::OSType ToOS,
40                        llvm::Triple::EnvironmentType ToEnv)
41        : Value(((StorageType(FromOS) * StorageType(llvm::Triple::LastOSType) +
42                  StorageType(FromEnv))
43                 << 32ull) |
44                (StorageType(ToOS) * StorageType(llvm::Triple::LastOSType) +
45                 StorageType(ToEnv))) {}
46
47    /// Returns the os-environment mapping pair that's used to represent the
48    /// macOS -> Mac Catalyst version mapping.
49    static inline constexpr OSEnvPair macOStoMacCatalystPair() {
50      return OSEnvPair(llvm::Triple::MacOSX, llvm::Triple::UnknownEnvironment,
51                       llvm::Triple::IOS, llvm::Triple::MacABI);
52    }
53
54    /// Returns the os-environment mapping pair that's used to represent the
55    /// Mac Catalyst -> macOS version mapping.
56    static inline constexpr OSEnvPair macCatalystToMacOSPair() {
57      return OSEnvPair(llvm::Triple::IOS, llvm::Triple::MacABI,
58                       llvm::Triple::MacOSX, llvm::Triple::UnknownEnvironment);
59    }
60
61    /// Returns the os-environment mapping pair that's used to represent the
62    /// iOS -> watchOS version mapping.
63    static inline constexpr OSEnvPair iOStoWatchOSPair() {
64      return OSEnvPair(llvm::Triple::IOS, llvm::Triple::UnknownEnvironment,
65                       llvm::Triple::WatchOS, llvm::Triple::UnknownEnvironment);
66    }
67
68    /// Returns the os-environment mapping pair that's used to represent the
69    /// iOS -> tvOS version mapping.
70    static inline constexpr OSEnvPair iOStoTvOSPair() {
71      return OSEnvPair(llvm::Triple::IOS, llvm::Triple::UnknownEnvironment,
72                       llvm::Triple::TvOS, llvm::Triple::UnknownEnvironment);
73    }
74
75  private:
76    StorageType Value;
77
78    friend class DarwinSDKInfo;
79  };
80
81  /// Represents a version mapping that maps from a version of one target to a
82  /// version of a related target.
83  ///
84  /// e.g. "macOS_iOSMac":{"10.15":"13.1"} is an example of a macOS -> Mac
85  /// Catalyst version map.
86  class RelatedTargetVersionMapping {
87  public:
88    RelatedTargetVersionMapping(
89        VersionTuple MinimumKeyVersion, VersionTuple MaximumKeyVersion,
90        VersionTuple MinimumValue, VersionTuple MaximumValue,
91        llvm::DenseMap<VersionTuple, VersionTuple> Mapping)
92        : MinimumKeyVersion(MinimumKeyVersion),
93          MaximumKeyVersion(MaximumKeyVersion), MinimumValue(MinimumValue),
94          MaximumValue(MaximumValue), Mapping(Mapping) {
95      assert(!this->Mapping.empty() && "unexpected empty mapping");
96    }
97
98    /// Returns the value with the lowest version in the mapping.
99    const VersionTuple &getMinimumValue() const { return MinimumValue; }
100
101    /// Returns the mapped key, or the appropriate Minimum / MaximumValue if
102    /// they key is outside of the mapping bounds. If they key isn't mapped, but
103    /// within the minimum and maximum bounds, std::nullopt is returned.
104    std::optional<VersionTuple>
105    map(const VersionTuple &Key, const VersionTuple &MinimumValue,
106        std::optional<VersionTuple> MaximumValue) const;
107
108    static std::optional<RelatedTargetVersionMapping>
109    parseJSON(const llvm::json::Object &Obj,
110              VersionTuple MaximumDeploymentTarget);
111
112  private:
113    VersionTuple MinimumKeyVersion;
114    VersionTuple MaximumKeyVersion;
115    VersionTuple MinimumValue;
116    VersionTuple MaximumValue;
117    llvm::DenseMap<VersionTuple, VersionTuple> Mapping;
118  };
119
120  DarwinSDKInfo(
121      VersionTuple Version, VersionTuple MaximumDeploymentTarget,
122      llvm::DenseMap<OSEnvPair::StorageType,
123                     std::optional<RelatedTargetVersionMapping>>
124          VersionMappings =
125              llvm::DenseMap<OSEnvPair::StorageType,
126                             std::optional<RelatedTargetVersionMapping>>())
127      : Version(Version), MaximumDeploymentTarget(MaximumDeploymentTarget),
128        VersionMappings(std::move(VersionMappings)) {}
129
130  const llvm::VersionTuple &getVersion() const { return Version; }
131
132  // Returns the optional, target-specific version mapping that maps from one
133  // target to another target.
134  //
135  // This mapping is constructed from an appropriate mapping in the SDKSettings,
136  // for instance, when building for Mac Catalyst, the mapping would contain the
137  // "macOS_iOSMac" mapping as it maps the macOS versions to the Mac Catalyst
138  // versions.
139  //
140  // This mapping does not exist when the target doesn't have an appropriate
141  // related version mapping, or when there was an error reading the mapping
142  // from the SDKSettings, or when it's missing in the SDKSettings.
143  const RelatedTargetVersionMapping *getVersionMapping(OSEnvPair Kind) const {
144    auto Mapping = VersionMappings.find(Kind.Value);
145    if (Mapping == VersionMappings.end())
146      return nullptr;
147    return Mapping->getSecond() ? &*Mapping->getSecond() : nullptr;
148  }
149
150  static std::optional<DarwinSDKInfo>
151  parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj);
152
153private:
154  VersionTuple Version;
155  VersionTuple MaximumDeploymentTarget;
156  // Need to wrap the value in an optional here as the value has to be default
157  // constructible, and std::unique_ptr doesn't like DarwinSDKInfo being
158  // Optional as Optional is trying to copy it in emplace.
159  llvm::DenseMap<OSEnvPair::StorageType,
160                 std::optional<RelatedTargetVersionMapping>>
161      VersionMappings;
162};
163
164/// Parse the SDK information from the SDKSettings.json file.
165///
166/// \returns an error if the SDKSettings.json file is invalid, std::nullopt if
167/// the SDK has no SDKSettings.json, or a valid \c DarwinSDKInfo otherwise.
168Expected<std::optional<DarwinSDKInfo>>
169parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath);
170
171} // end namespace clang
172
173#endif // LLVM_CLANG_BASIC_DARWINSDKINFO_H
174