|
36 | 36 | #include "swift/SIL/TypeLowering.h"
|
37 | 37 | #include "clang/Basic/TargetInfo.h"
|
38 | 38 | #include "llvm/ADT/StringSet.h"
|
| 39 | +#include "llvm/ADT/StringSwitch.h" |
39 | 40 | #include "llvm/IR/Mangler.h"
|
40 | 41 | #include "llvm/Support/Error.h"
|
41 | 42 | #include "llvm/Support/Process.h"
|
42 | 43 | #include "llvm/Support/YAMLTraits.h"
|
| 44 | +#include "llvm/Support/YAMLParser.h" |
43 | 45 | #include "llvm/TextAPI/MachO/InterfaceFile.h"
|
44 | 46 | #include "llvm/TextAPI/MachO/TextAPIReader.h"
|
45 | 47 | #include "llvm/TextAPI/MachO/TextAPIWriter.h"
|
|
49 | 51 | using namespace swift;
|
50 | 52 | using namespace swift::irgen;
|
51 | 53 | using namespace swift::tbdgen;
|
| 54 | +using namespace llvm::yaml; |
52 | 55 | using StringSet = llvm::StringSet<>;
|
53 | 56 | using SymbolKind = llvm::MachO::SymbolKind;
|
54 | 57 |
|
@@ -79,6 +82,152 @@ static Optional<llvm::VersionTuple> getDeclMoveOSVersion(Decl *D) {
|
79 | 82 | return None;
|
80 | 83 | }
|
81 | 84 |
|
| 85 | +enum class LinkerPlatformId: uint8_t { |
| 86 | +#define LD_PLATFORM(Name, Id) Name = Id, |
| 87 | +#include "ldPlatformKinds.def" |
| 88 | +}; |
| 89 | + |
| 90 | +static StringRef getLinkerPlatformName(uint8_t Id) { |
| 91 | + switch (Id) { |
| 92 | +#define LD_PLATFORM(Name, Id) case Id: return #Name; |
| 93 | +#include "ldPlatformKinds.def" |
| 94 | + default: |
| 95 | + llvm_unreachable("unrecognized platform id"); |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +static Optional<uint8_t> getLinkerPlatformId(StringRef Platform) { |
| 100 | + return llvm::StringSwitch<Optional<uint8_t>>(Platform) |
| 101 | +#define LD_PLATFORM(Name, Id) .Case(#Name, Id) |
| 102 | +#include "ldPlatformKinds.def" |
| 103 | + .Default(None); |
| 104 | +} |
| 105 | + |
| 106 | +struct InstallNameStore { |
| 107 | + // The default install name to use when no specific install name is specified. |
| 108 | + std::string InstallName; |
| 109 | + // The install name specific to the platform id. This takes precedence over |
| 110 | + // the default install name. |
| 111 | + std::map<uint8_t, std::string> PlatformInstallName; |
| 112 | + StringRef getInstallName(LinkerPlatformId Id) const { |
| 113 | + auto It = PlatformInstallName.find((uint8_t)Id); |
| 114 | + if (It == PlatformInstallName.end()) |
| 115 | + return InstallName; |
| 116 | + else |
| 117 | + return It->second; |
| 118 | + } |
| 119 | + void remark(ASTContext &Ctx, StringRef ModuleName) const { |
| 120 | + Ctx.Diags.diagnose(SourceLoc(), diag::default_previous_install_name, |
| 121 | + ModuleName, InstallName); |
| 122 | + for (auto Pair: PlatformInstallName) { |
| 123 | + Ctx.Diags.diagnose(SourceLoc(), diag::platform_previous_install_name, |
| 124 | + ModuleName, getLinkerPlatformName(Pair.first), |
| 125 | + Pair.second); |
| 126 | + } |
| 127 | + } |
| 128 | +}; |
| 129 | + |
| 130 | +static std::string getScalaNodeText(Node *N) { |
| 131 | + SmallString<32> Buffer; |
| 132 | + return cast<ScalarNode>(N)->getValue(Buffer).str(); |
| 133 | +} |
| 134 | + |
| 135 | +static std::set<int8_t> getSequenceNodePlatformList(ASTContext &Ctx, Node *N) { |
| 136 | + std::set<int8_t> Results; |
| 137 | + for (auto &E: *cast<SequenceNode>(N)) { |
| 138 | + auto Platform = getScalaNodeText(&E); |
| 139 | + auto Id = getLinkerPlatformId(Platform); |
| 140 | + if (Id.hasValue()) { |
| 141 | + Results.insert(*Id); |
| 142 | + } else { |
| 143 | + // Diagnose unrecognized platform name. |
| 144 | + Ctx.Diags.diagnose(SourceLoc(), diag::unknown_platform_name, Platform); |
| 145 | + } |
| 146 | + } |
| 147 | + return Results; |
| 148 | +} |
| 149 | + |
| 150 | +/// Parse an entry like this, where the "platforms" key-value pair is optional: |
| 151 | +/// { |
| 152 | +/// "module": "Foo", |
| 153 | +/// "platforms": ["macOS"], |
| 154 | +/// "install_name": "/System/MacOS" |
| 155 | +/// }, |
| 156 | +static int |
| 157 | +parseEntry(ASTContext &Ctx, |
| 158 | + Node *Node, std::map<std::string, InstallNameStore> &Stores) { |
| 159 | + if (auto *SN = cast<SequenceNode>(Node)) { |
| 160 | + for (auto It = SN->begin(); It != SN->end(); ++It) { |
| 161 | + auto *MN = cast<MappingNode>(&*It); |
| 162 | + std::string ModuleName; |
| 163 | + std::string InstallName; |
| 164 | + Optional<std::set<int8_t>> Platforms; |
| 165 | + for (auto &Pair: *MN) { |
| 166 | + auto Key = getScalaNodeText(Pair.getKey()); |
| 167 | + auto* Value = Pair.getValue(); |
| 168 | + if (Key == "module") { |
| 169 | + ModuleName = getScalaNodeText(Value); |
| 170 | + } else if (Key == "platforms") { |
| 171 | + Platforms = getSequenceNodePlatformList(Ctx, Value); |
| 172 | + } else if (Key == "install_name") { |
| 173 | + InstallName = getScalaNodeText(Value); |
| 174 | + } else { |
| 175 | + return 1; |
| 176 | + } |
| 177 | + } |
| 178 | + if (ModuleName.empty() || InstallName.empty()) |
| 179 | + return 1; |
| 180 | + auto &Store = Stores.insert(std::make_pair(ModuleName, |
| 181 | + InstallNameStore())).first->second; |
| 182 | + if (Platforms.hasValue()) { |
| 183 | + // This install name is platform-specific. |
| 184 | + for (auto Id: Platforms.getValue()) { |
| 185 | + Store.PlatformInstallName[Id] = InstallName; |
| 186 | + } |
| 187 | + } else { |
| 188 | + // The install name is the default one. |
| 189 | + Store.InstallName = InstallName; |
| 190 | + } |
| 191 | + } |
| 192 | + } else { |
| 193 | + return 1; |
| 194 | + } |
| 195 | + return 0; |
| 196 | +} |
| 197 | + |
| 198 | +static std::map<std::string, InstallNameStore> |
| 199 | +parsePreviousModuleInstallNameMap(ASTContext &Ctx, StringRef FileName) { |
| 200 | + namespace yaml = llvm::yaml; |
| 201 | + std::map<std::string, InstallNameStore> AllInstallNames; |
| 202 | + SWIFT_DEFER { |
| 203 | + for (auto Pair: AllInstallNames) { |
| 204 | + Pair.second.remark(Ctx, Pair.first); |
| 205 | + } |
| 206 | + }; |
| 207 | + // Load the input file. |
| 208 | + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr = |
| 209 | + vfs::getFileOrSTDIN(*Ctx.SourceMgr.getFileSystem(), FileName); |
| 210 | + if (!FileBufOrErr) { |
| 211 | + Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_missing, |
| 212 | + FileName); |
| 213 | + return AllInstallNames; |
| 214 | + } |
| 215 | + StringRef Buffer = FileBufOrErr->get()->getBuffer(); |
| 216 | + yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, FileName), |
| 217 | + Ctx.SourceMgr.getLLVMSourceMgr()); |
| 218 | + for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) { |
| 219 | + assert(DI != Stream.end() && "Failed to read a document"); |
| 220 | + yaml::Node *N = DI->getRoot(); |
| 221 | + assert(N && "Failed to find a root"); |
| 222 | + if (parseEntry(Ctx, N, AllInstallNames)) { |
| 223 | + Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_corrupted, |
| 224 | + FileName); |
| 225 | + return AllInstallNames; |
| 226 | + } |
| 227 | + } |
| 228 | + return AllInstallNames; |
| 229 | +} |
| 230 | + |
82 | 231 | void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name,
|
83 | 232 | llvm::MachO::SymbolKind kind) {
|
84 | 233 | if (kind != llvm::MachO::SymbolKind::GlobalSymbol)
|
@@ -733,6 +882,10 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile,
|
733 | 882 | file.setCompatibilityVersion(*packed);
|
734 | 883 | }
|
735 | 884 |
|
| 885 | + if (!opts.ModuleInstallNameMapPath.empty()) { |
| 886 | + parsePreviousModuleInstallNameMap(ctx, opts.ModuleInstallNameMapPath); |
| 887 | + } |
| 888 | + |
736 | 889 | llvm::MachO::Target target(triple);
|
737 | 890 | file.addTarget(target);
|
738 | 891 |
|
|
0 commit comments