Skip to content

TBDGen: teach the compiler to take a json file indicating previous install names #29179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,28 @@ WARNING(tbd_only_supported_in_whole_module,none,
"TBD generation is only supported when the whole module can be seen",
())

WARNING(linker_directives_choice_confusion,none,
"only one of -emit-ldadd-cfile-path and -module-installname-map-file can be specified;"
"the c file won't be generated",
())

ERROR(previous_installname_map_missing,none,
"cannot open previous install name map from %0",
(StringRef))

ERROR(previous_installname_map_corrupted,none,
"previous install name map from %0 is malformed",
(StringRef))

REMARK(default_previous_install_name, none,
"default previous install name for %0 is %1", (StringRef, StringRef))

REMARK(platform_previous_install_name, none,
"previous install name for %0 in %1 is %2", (StringRef, StringRef, StringRef))

ERROR(unknown_platform_name, none,
"unkown platform name %0", (StringRef))

ERROR(symbol_in_tbd_not_in_ir,none,
"symbol '%0' (%1) is in TBD file, but not in generated IR",
(StringRef, StringRef))
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -658,4 +658,8 @@ def type_info_dump_filter_EQ : Joined<["-"], "type-info-dump-filter=">,
def emit_ldadd_cfile_path
: Separate<["-"], "emit-ldadd-cfile-path">, MetaVarName<"<path>">,
HelpText<"Generate .c file defining symbols to add back">;

def previous_module_installname_map_file
: Separate<["-"], "previous-module-installname-map-file">, MetaVarName<"<path>">,
HelpText<"Path to a Json file indicating module name to installname map for @_originallyDefinedIn">;
} // end let Flags = [FrontendOption, NoDriverOption, HelpHidden]
4 changes: 4 additions & 0 deletions include/swift/TBDGen/TBDGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ struct TBDGenOptions {
/// The dylib compatibility-version to use in the generated TBD file. Defaults
/// to empty string if not provided.
std::string CompatibilityVersion;

/// The path to a Json file indicating the module name to install-name map
/// used by @_originallyDefinedIn
std::string ModuleInstallNameMapPath;
};

void enumeratePublicSymbols(FileUnit *module, llvm::StringSet<> &symbols,
Expand Down
3 changes: 3 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,9 @@ static bool ParseTBDGenArgs(TBDGenOptions &Opts, ArgList &Args,
if (const Arg *A = Args.getLastArg(OPT_tbd_current_version)) {
Opts.CurrentVersion = A->getValue();
}
if (const Arg *A = Args.getLastArg(OPT_previous_module_installname_map_file)) {
Opts.ModuleInstallNameMapPath = A->getValue();
}
return false;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/FrontendTool/FrontendTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,11 @@ static bool writeLdAddCFileIfNeeded(CompilerInvocation &Invocation,
diag::tbd_only_supported_in_whole_module);
return true;
}
if (!Invocation.getTBDGenOptions().ModuleInstallNameMapPath.empty()) {
Instance.getDiags().diagnose(SourceLoc(),
diag::linker_directives_choice_confusion);
return true;
}
auto tbdOpts = Invocation.getTBDGenOptions();
tbdOpts.LinkerDirectivesOnly = true;
llvm::StringSet<> ldSymbols;
Expand Down
153 changes: 153 additions & 0 deletions lib/TBDGen/TBDGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
#include "swift/SIL/TypeLowering.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/Mangler.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/TextAPI/MachO/InterfaceFile.h"
#include "llvm/TextAPI/MachO/TextAPIReader.h"
#include "llvm/TextAPI/MachO/TextAPIWriter.h"
Expand All @@ -49,6 +51,7 @@
using namespace swift;
using namespace swift::irgen;
using namespace swift::tbdgen;
using namespace llvm::yaml;
using StringSet = llvm::StringSet<>;
using SymbolKind = llvm::MachO::SymbolKind;

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

enum class LinkerPlatformId: uint8_t {
#define LD_PLATFORM(Name, Id) Name = Id,
#include "ldPlatformKinds.def"
};

static StringRef getLinkerPlatformName(uint8_t Id) {
switch (Id) {
#define LD_PLATFORM(Name, Id) case Id: return #Name;
#include "ldPlatformKinds.def"
default:
llvm_unreachable("unrecognized platform id");
}
}

static Optional<uint8_t> getLinkerPlatformId(StringRef Platform) {
return llvm::StringSwitch<Optional<uint8_t>>(Platform)
#define LD_PLATFORM(Name, Id) .Case(#Name, Id)
#include "ldPlatformKinds.def"
.Default(None);
}

struct InstallNameStore {
// The default install name to use when no specific install name is specified.
std::string InstallName;
// The install name specific to the platform id. This takes precedence over
// the default install name.
std::map<uint8_t, std::string> PlatformInstallName;
StringRef getInstallName(LinkerPlatformId Id) const {
auto It = PlatformInstallName.find((uint8_t)Id);
if (It == PlatformInstallName.end())
return InstallName;
else
return It->second;
}
void remark(ASTContext &Ctx, StringRef ModuleName) const {
Ctx.Diags.diagnose(SourceLoc(), diag::default_previous_install_name,
ModuleName, InstallName);
for (auto Pair: PlatformInstallName) {
Ctx.Diags.diagnose(SourceLoc(), diag::platform_previous_install_name,
ModuleName, getLinkerPlatformName(Pair.first),
Pair.second);
}
}
};

static std::string getScalaNodeText(Node *N) {
SmallString<32> Buffer;
return cast<ScalarNode>(N)->getValue(Buffer).str();
}

static std::set<int8_t> getSequenceNodePlatformList(ASTContext &Ctx, Node *N) {
std::set<int8_t> Results;
for (auto &E: *cast<SequenceNode>(N)) {
auto Platform = getScalaNodeText(&E);
auto Id = getLinkerPlatformId(Platform);
if (Id.hasValue()) {
Results.insert(*Id);
} else {
// Diagnose unrecognized platform name.
Ctx.Diags.diagnose(SourceLoc(), diag::unknown_platform_name, Platform);
}
}
return Results;
}

/// Parse an entry like this, where the "platforms" key-value pair is optional:
/// {
/// "module": "Foo",
/// "platforms": ["macOS"],
/// "install_name": "/System/MacOS"
/// },
static int
parseEntry(ASTContext &Ctx,
Node *Node, std::map<std::string, InstallNameStore> &Stores) {
if (auto *SN = cast<SequenceNode>(Node)) {
for (auto It = SN->begin(); It != SN->end(); ++It) {
auto *MN = cast<MappingNode>(&*It);
std::string ModuleName;
std::string InstallName;
Optional<std::set<int8_t>> Platforms;
for (auto &Pair: *MN) {
auto Key = getScalaNodeText(Pair.getKey());
auto* Value = Pair.getValue();
if (Key == "module") {
ModuleName = getScalaNodeText(Value);
} else if (Key == "platforms") {
Platforms = getSequenceNodePlatformList(Ctx, Value);
} else if (Key == "install_name") {
InstallName = getScalaNodeText(Value);
} else {
return 1;
}
}
if (ModuleName.empty() || InstallName.empty())
return 1;
auto &Store = Stores.insert(std::make_pair(ModuleName,
InstallNameStore())).first->second;
if (Platforms.hasValue()) {
// This install name is platform-specific.
for (auto Id: Platforms.getValue()) {
Store.PlatformInstallName[Id] = InstallName;
}
} else {
// The install name is the default one.
Store.InstallName = InstallName;
}
}
} else {
return 1;
}
return 0;
}

static std::map<std::string, InstallNameStore>
parsePreviousModuleInstallNameMap(ASTContext &Ctx, StringRef FileName) {
namespace yaml = llvm::yaml;
std::map<std::string, InstallNameStore> AllInstallNames;
SWIFT_DEFER {
for (auto Pair: AllInstallNames) {
Pair.second.remark(Ctx, Pair.first);
}
};
// Load the input file.
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
vfs::getFileOrSTDIN(*Ctx.SourceMgr.getFileSystem(), FileName);
if (!FileBufOrErr) {
Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_missing,
FileName);
return AllInstallNames;
}
StringRef Buffer = FileBufOrErr->get()->getBuffer();
yaml::Stream Stream(llvm::MemoryBufferRef(Buffer, FileName),
Ctx.SourceMgr.getLLVMSourceMgr());
for (auto DI = Stream.begin(); DI != Stream.end(); ++ DI) {
assert(DI != Stream.end() && "Failed to read a document");
yaml::Node *N = DI->getRoot();
assert(N && "Failed to find a root");
if (parseEntry(Ctx, N, AllInstallNames)) {
Ctx.Diags.diagnose(SourceLoc(), diag::previous_installname_map_corrupted,
FileName);
return AllInstallNames;
}
}
return AllInstallNames;
}

void TBDGenVisitor::addLinkerDirectiveSymbols(StringRef name,
llvm::MachO::SymbolKind kind) {
if (kind != llvm::MachO::SymbolKind::GlobalSymbol)
Expand Down Expand Up @@ -733,6 +882,10 @@ static void enumeratePublicSymbolsAndWrite(ModuleDecl *M, FileUnit *singleFile,
file.setCompatibilityVersion(*packed);
}

if (!opts.ModuleInstallNameMapPath.empty()) {
parsePreviousModuleInstallNameMap(ctx, opts.ModuleInstallNameMapPath);
}

llvm::MachO::Target target(triple);
file.addTarget(target);

Expand Down
30 changes: 30 additions & 0 deletions lib/TBDGen/ldPlatformKinds.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===--- ldPlatformKinds.def - Compiler declaration metaprogramming --*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines platform IDs used to emit linker directives $ld$previous
//
//===----------------------------------------------------------------------===//

#ifndef LD_PLATFORM
# define LD_PLATFORM(Name, Id)
#endif

LD_PLATFORM(macOS, 1)
LD_PLATFORM(iOS, 2)
LD_PLATFORM(tvOS, 3)
LD_PLATFORM(watchOS, 4)
LD_PLATFORM(macCatalyst, 6)
LD_PLATFORM(iOS_sim, 7)
LD_PLATFORM(tvOS_sim, 8)
LD_PLATFORM(watchOS_sim, 9)

#undef LD_PLATFORM
16 changes: 16 additions & 0 deletions test/TBD/Inputs/install-name-map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"module": "Foo",
"platforms": ["macOS"],
"install_name": "/System/MacOS"
},
{
"module": "Foo",
"install_name": "/System/default"
},
{
"module": "Foo",
"platforms": ["iOS", "tvOS", "watchOS"],
"install_name": "/System/Others"
},
]
13 changes: 13 additions & 0 deletions test/TBD/previous-install-name-map.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// REQUIRES: VENDOR=apple
// REQUIRES: OS=macosx

// RUN: %empty-directory(%t)

// 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
// RUN: %FileCheck %s < %t/remark.txt

// CHECK: remark: default previous install name for Foo is /System/default
// CHECK: remark: previous install name for Foo in macOS is /System/MacOS
// CHECK: remark: previous install name for Foo in iOS is /System/Others
// CHECK: remark: previous install name for Foo in tvOS is /System/Others
// CHECK: remark: previous install name for Foo in watchOS is /System/Others