Skip to content

Commit 90f59e4

Browse files
committed
TBDGen: teach the compiler to take a json file indicating previous install names
Using the new linker directives $ld$previous requires the compiler to know the previous install names for the symbols marked as removed. This patch teaches the compiler to take a path to a Json file specifying the map between module names and previous install names. Also, these install names can be platform-specific. Progress towards: rdar://58281536
1 parent 7928c58 commit 90f59e4

File tree

9 files changed

+250
-0
lines changed

9 files changed

+250
-0
lines changed

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,28 @@ WARNING(tbd_only_supported_in_whole_module,none,
260260
"TBD generation is only supported when the whole module can be seen",
261261
())
262262

263+
WARNING(linker_directives_choice_confusion,none,
264+
"only one of -emit-ldadd-cfile-path and -module-installname-map-file can be specified;"
265+
"the c file won't be generated",
266+
())
267+
268+
ERROR(previous_installname_map_missing,none,
269+
"cannot open previous install name map from %0",
270+
(StringRef))
271+
272+
ERROR(previous_installname_map_corrupted,none,
273+
"previous install name map from %0 is malformed",
274+
(StringRef))
275+
276+
REMARK(default_previous_install_name, none,
277+
"default previous install name for %0 is %1", (StringRef, StringRef))
278+
279+
REMARK(platform_previous_install_name, none,
280+
"previous install name for %0 in %1 is %2", (StringRef, StringRef, StringRef))
281+
282+
ERROR(unknown_platform_name, none,
283+
"unkown platform name %0", (StringRef))
284+
263285
ERROR(symbol_in_tbd_not_in_ir,none,
264286
"symbol '%0' (%1) is in TBD file, but not in generated IR",
265287
(StringRef, StringRef))

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,4 +658,8 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">,
658658
def emit_ldadd_cfile_path
659659
: Separate<["-"], "emit-ldadd-cfile-path">, MetaVarName<"<path>">,
660660
HelpText<"Generate .c file defining symbols to add back">;
661+
662+
def previous_module_installname_map_file
663+
: Separate<["-"], "previous-module-installname-map-file">, MetaVarName<"<path>">,
664+
HelpText<"Path to a Json file indicating module name to installname map for @_originallyDefinedIn">;
661665
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]

include/swift/TBDGen/TBDGen.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ struct TBDGenOptions {
4949
/// The dylib compatibility-version to use in the generated TBD file. Defaults
5050
/// to empty string if not provided.
5151
std::string CompatibilityVersion;
52+
53+
/// The path to a Json file indicating the module name to install-name map
54+
/// used by @_originallyDefinedIn
55+
std::string ModuleInstallNameMapPath;
5256
};
5357

5458
void enumeratePublicSymbols(FileUnit *module, llvm::StringSet<> &symbols,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,9 @@ static bool ParseTBDGenArgs(TBDGenOptions &Opts, ArgList &Args,
10181018
if (const Arg *A = Args.getLastArg(OPT_tbd_current_version)) {
10191019
Opts.CurrentVersion = A->getValue();
10201020
}
1021+
if (const Arg *A = Args.getLastArg(OPT_previous_module_installname_map_file)) {
1022+
Opts.ModuleInstallNameMapPath = A->getValue();
1023+
}
10211024
return false;
10221025
}
10231026

lib/FrontendTool/FrontendTool.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,11 @@ static bool writeLdAddCFileIfNeeded(CompilerInvocation &Invocation,
10681068
diag::tbd_only_supported_in_whole_module);
10691069
return true;
10701070
}
1071+
if (!Invocation.getTBDGenOptions().ModuleInstallNameMapPath.empty()) {
1072+
Instance.getDiags().diagnose(SourceLoc(),
1073+
diag::linker_directives_choice_confusion);
1074+
return true;
1075+
}
10711076
auto tbdOpts = Invocation.getTBDGenOptions();
10721077
tbdOpts.LinkerDirectivesOnly = true;
10731078
llvm::StringSet<> ldSymbols;

lib/TBDGen/TBDGen.cpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
#include "swift/SIL/TypeLowering.h"
3737
#include "clang/Basic/TargetInfo.h"
3838
#include "llvm/ADT/StringSet.h"
39+
#include "llvm/ADT/StringSwitch.h"
3940
#include "llvm/IR/Mangler.h"
4041
#include "llvm/Support/Error.h"
4142
#include "llvm/Support/Process.h"
4243
#include "llvm/Support/YAMLTraits.h"
44+
#include "llvm/Support/YAMLParser.h"
4345
#include "llvm/TextAPI/MachO/InterfaceFile.h"
4446
#include "llvm/TextAPI/MachO/TextAPIReader.h"
4547
#include "llvm/TextAPI/MachO/TextAPIWriter.h"
@@ -49,6 +51,7 @@
4951
using namespace swift;
5052
using namespace swift::irgen;
5153
using namespace swift::tbdgen;
54+
using namespace llvm::yaml;
5255
using StringSet = llvm::StringSet<>;
5356
using SymbolKind = llvm::MachO::SymbolKind;
5457

@@ -79,6 +82,152 @@ static Optional<llvm::VersionTuple> getDeclMoveOSVersion(Decl *D) {
7982
return None;
8083
}
8184

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+
82231
void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name,
83232
llvm::MachO::SymbolKind kind) {
84233
if (kind != llvm::MachO::SymbolKind::GlobalSymbol)
@@ -733,6 +882,10 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile,
733882
file.setCompatibilityVersion(*packed);
734883
}
735884

885+
if (!opts.ModuleInstallNameMapPath.empty()) {
886+
parsePreviousModuleInstallNameMap(ctx, opts.ModuleInstallNameMapPath);
887+
}
888+
736889
llvm::MachO::Target target(triple);
737890
file.addTarget(target);
738891

lib/TBDGen/ldPlatformKinds.def

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- ldPlatformKinds.def - Compiler declaration metaprogramming --*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines platform IDs used to emit linker directives $ld$previous
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef LD_PLATFORM
18+
# define LD_PLATFORM(Name, Id)
19+
#endif
20+
21+
LD_PLATFORM(macOS, 1)
22+
LD_PLATFORM(iOS, 2)
23+
LD_PLATFORM(tvOS, 3)
24+
LD_PLATFORM(watchOS, 4)
25+
LD_PLATFORM(macCatalyst, 6)
26+
LD_PLATFORM(iOS_sim, 7)
27+
LD_PLATFORM(tvOS_sim, 8)
28+
LD_PLATFORM(watchOS_sim, 9)
29+
30+
#undef LD_PLATFORM

test/TBD/Inputs/install-name-map.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
{
3+
"module": "Foo",
4+
"platforms": ["macOS"],
5+
"install_name": "/System/MacOS"
6+
},
7+
{
8+
"module": "Foo",
9+
"install_name": "/System/default"
10+
},
11+
{
12+
"module": "Foo",
13+
"platforms": ["iOS", "tvOS", "watchOS"],
14+
"install_name": "/System/Others"
15+
},
16+
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// REQUIRES: VENDOR=apple
2+
// REQUIRES: OS=macosx
3+
4+
// RUN: %empty-directory(%t)
5+
6+
// RUN: %target-swift-frontend -typecheck %s -emit-tbd -emit-tbd-path %t/linker_directives.tbd -previous-module-installname-map-file %S/Inputs/install-name-map.json >& %t/remark.txt
7+
// RUN: %FileCheck %s < %t/remark.txt
8+
9+
// CHECK: remark: default previous install name for Foo is /System/default
10+
// CHECK: remark: previous install name for Foo in macOS is /System/MacOS
11+
// CHECK: remark: previous install name for Foo in iOS is /System/Others
12+
// CHECK: remark: previous install name for Foo in tvOS is /System/Others
13+
// CHECK: remark: previous install name for Foo in watchOS is /System/Others

0 commit comments

Comments
 (0)