Extract autolink information as a compile step.

Swift SVN r25510
This commit is contained in:
Graham Batty
2015-02-24 20:33:05 +00:00
parent 49548aee09
commit 078a558b26
22 changed files with 344 additions and 16 deletions

View File

@@ -38,6 +38,7 @@ public:
Input = 0,
CompileJob,
MergeModuleJob,
AutolinkExtractJob,
REPLJob,
LinkJob,
GenerateDSYMJob,
@@ -176,6 +177,17 @@ public:
}
};
class AutolinkExtractJobAction : public JobAction {
virtual void anchor();
public:
AutolinkExtractJobAction(ArrayRef<Action *> Inputs)
: JobAction(Action::AutolinkExtractJob, Inputs, types::TY_AutolinkFile) {}
static bool classof(const Action *A) {
return A->getKind() == Action::AutolinkExtractJob;
}
};
class GenerateDSYMJobAction : public JobAction {
virtual void anchor();
public:

View File

@@ -110,9 +110,10 @@ public:
/// DriverKind determines how later arguments are parsed, as well as the
/// allowable OutputInfo::Mode values.
enum class DriverKind {
Interactive, // swift
Batch, // swiftc
UpdateCode, // swift-update
Interactive, // swift
Batch, // swiftc
UpdateCode, // swift-update
AutolinkExtract, // swift-autolink-extract
};
private:

View File

@@ -44,12 +44,14 @@ class ToolChain {
mutable std::unique_ptr<Tool> LLDB;
mutable std::unique_ptr<Tool> Linker;
mutable std::unique_ptr<Tool> Dsymutil;
mutable std::unique_ptr<Tool> AutolinkExtract;
Tool *getSwift() const;
Tool *getMergeModule() const;
Tool *getLLDB() const;
Tool *getLinker() const;
Tool *getDsymutil() const;
Tool *getAutolinkExtract() const;
protected:
ToolChain(const Driver &D, const llvm::Triple &T) : D(D), Triple(T) {}

View File

@@ -44,6 +44,7 @@ TYPE("image", Image, "out", "")
TYPE("object", Object, "o", "")
TYPE("dSYM", dSYM, "dSYM", "")
TYPE("dependencies", Dependencies, "d", "")
TYPE("autolink", AutolinkFile, "autolink", "")
TYPE("swiftmodule", SwiftModuleFile, "swiftmodule", "")
TYPE("swiftdoc", SwiftModuleDocFile, "swiftdoc", "")
TYPE("assembly", Assembly, "s", "")

View File

@@ -30,7 +30,8 @@ namespace options {
NoDriverOption = (1 << 5),
NoInteractiveOption = (1 << 6),
NoBatchOption = (1 << 7),
DoesNotAffectIncrementalBuild = (1 << 8)
DoesNotAffectIncrementalBuild = (1 << 8),
AutolinkExtractOption = (1 << 9)
};
enum ID {

View File

@@ -23,6 +23,9 @@ include "llvm/Option/OptParser.td"
// The option should be accepted by swift -frontend.
def FrontendOption : OptionFlag;
// The option should be accepted by swift-autolink-extract
def AutolinkExtractOption : OptionFlag;
// The option should not be accepted by the driver.
def NoDriverOption : OptionFlag;
@@ -83,7 +86,8 @@ def driver_use_frontend_path : Separate<["-"], "driver-use-frontend-path">,
def driver_mode : Joined<["--"], "driver-mode=">, Flags<[HelpHidden]>,
HelpText<"Set the driver mode to either 'swift' or 'swiftc' or 'swift-update'">;
def help : Flag<["-", "--"], "help">, Flags<[FrontendOption]>,
def help : Flag<["-", "--"], "help">,
Flags<[FrontendOption, AutolinkExtractOption]>,
HelpText<"Display available options">;
def h : Flag<["-"], "h">, Alias<help>;
def help_hidden : Flag<["-", "--"], "help-hidden">,
@@ -104,7 +108,7 @@ def _DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>,
Flags<[FrontendOption, DoesNotAffectIncrementalBuild]>;
def o : JoinedOrSeparate<["-"], "o">,
Flags<[FrontendOption, NoInteractiveOption]>,
Flags<[FrontendOption, AutolinkExtractOption, NoInteractiveOption]>,
HelpText<"Write output to <file>">, MetaVarName<"<file>">;
def j : JoinedOrSeparate<["-"], "j">, Flags<[DoesNotAffectIncrementalBuild]>,

View File

@@ -29,6 +29,7 @@ const char *Action::getClassName(ActionClass AC) {
case Input: return "input";
case CompileJob: return "compile";
case MergeModuleJob: return "merge-module";
case AutolinkExtractJob: return "swift-autolink-extract";
case REPLJob: return "repl";
case LinkJob: return "link";
case GenerateDSYMJob: return "generate-dSYM";
@@ -45,6 +46,8 @@ void CompileJobAction::anchor() {}
void MergeModuleJobAction::anchor() {}
void AutolinkExtractJobAction::anchor() {}
void REPLJobAction::anchor() {}
void LinkJobAction::anchor() {}

View File

@@ -93,6 +93,7 @@ void Driver::parseDriverKind(ArrayRef<const char *> Args) {
.Case("swift", DriverKind::Interactive)
.Case("swiftc", DriverKind::Batch)
.Case("swift-update", DriverKind::UpdateCode)
.Case("swift-autolink-extract", DriverKind::AutolinkExtract)
.Default(None);
if (Kind.hasValue())
@@ -1009,6 +1010,7 @@ void Driver::buildActions(const ToolChain &TC,
if (OI.ShouldGenerateModule)
break;
SWIFT_FALLTHROUGH;
case types::TY_AutolinkFile:
case types::TY_Object:
// Object inputs are only okay if linking.
if (OI.shouldLink())
@@ -1092,6 +1094,18 @@ void Driver::buildActions(const ToolChain &TC,
if (OI.shouldLink()) {
Action *LinkAction = new LinkJobAction(CompileActions, OI.LinkAction);
if (TC.getTriple().getObjectFormat() == llvm::Triple::ELF) {
// On ELF platforms there's no built in autolinking mechanism, so we
// pull the info we need from the .o files directly and pass them as an
// argument input file to the linker.
Action *AutolinkExtractAction = new AutolinkExtractJobAction(CompileActions);
// Takes the same inputs as the linker, but doesn't own them.
AutolinkExtractAction->setOwnsInputs(false);
// And gives its output to the linker.
LinkAction->addInput(AutolinkExtractAction);
}
if (MergeModuleAction) {
// We have a MergeModuleJobAction; this needs to be an input to the
// LinkJobAction. It shares inputs with the LinkAction, so tell it that it
@@ -1767,6 +1781,7 @@ void Driver::printHelp(bool ShowHidden) const {
break;
case DriverKind::Batch:
case DriverKind::UpdateCode:
case DriverKind::AutolinkExtract:
ExcludedFlagsBitmask |= options::NoBatchOption;
break;
}

View File

@@ -36,6 +36,7 @@ CACHE_TOOL(Swift)
CACHE_TOOL(MergeModule)
CACHE_TOOL(LLDB)
CACHE_TOOL(Dsymutil)
CACHE_TOOL(AutolinkExtract)
Tool *ToolChain::getLinker() const {
if (!Linker)
@@ -53,6 +54,8 @@ Tool *ToolChain::selectTool(const JobAction &JA) const {
return getLinker();
case Action::GenerateDSYMJob:
return getDsymutil();
case Action::AutolinkExtractJob:
return getAutolinkExtract();
case Action::REPLJob:
switch (cast<REPLJobAction>(JA).getRequestedMode()) {
case REPLJobAction::Mode::Integrated:

View File

@@ -195,6 +195,7 @@ Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
break;
case types::TY_Swift:
case types::TY_dSYM:
case types::TY_AutolinkFile:
case types::TY_Dependencies:
case types::TY_SwiftModuleDocFile:
case types::TY_ClangModuleFile:
@@ -477,6 +478,25 @@ Job *Dsymutil::constructJob(const JobAction &JA,
Arguments);
}
Job *AutolinkExtract::constructJob(const JobAction &JA,
std::unique_ptr<JobList> Inputs,
std::unique_ptr<CommandOutput> Output,
const ActionList &InputActions,
const ArgList &Args,
const OutputInfo &OI) const {
assert(Output->getPrimaryOutputType() == types::TY_AutolinkFile);
ArgStringList Arguments;
addPrimaryInputsOfType(Arguments, *Inputs, types::TY_Object);
addInputsOfType(Arguments, InputActions, types::TY_Object);
Arguments.push_back("-o");
Arguments.push_back(Args.MakeArgString(Output->getPrimaryOutputFilename()));
return new Job(JA, *this, std::move(Inputs), std::move(Output), getPath(),
Arguments);
}
/// Darwin Tools
llvm::Triple::ArchType darwin::getArchTypeForDarwinArchName(StringRef Arch) {
@@ -718,8 +738,17 @@ Job *linux::Linker::constructJob(const JobAction &JA,
Arguments.push_back("-Xlinker");
Arguments.push_back(Args.MakeArgString(RuntimeLibPath));
// Always add the stdlib
Arguments.push_back("-lswiftCore");
// Add any autolinking scripts to the arguments
for (const Job *Cmd : *Inputs) {
auto &OutputInfo = Cmd->getOutput();
if (OutputInfo.getPrimaryOutputType() == types::TY_AutolinkFile)
Arguments.push_back(Args.MakeArgString(
Twine("@") + OutputInfo.getPrimaryOutputFilename()));
}
// Add the linker script that coalesces protocol conformance sections.
Arguments.push_back("-Xlinker");
Arguments.push_back("-T");

View File

@@ -105,6 +105,18 @@ public:
const OutputInfo &OI) const;
};
class LLVM_LIBRARY_VISIBILITY AutolinkExtract : public ToolchainTool {
public:
explicit AutolinkExtract(const ToolChain &TC)
: ToolchainTool("swift-autolink-extract", TC) {}
virtual Job *constructJob(const JobAction &JA,
std::unique_ptr<JobList> Inputs,
std::unique_ptr<CommandOutput> Output,
const ActionList &InputActions,
const llvm::opt::ArgList &Args,
const OutputInfo &OI) const;
};
namespace darwin {

View File

@@ -73,6 +73,7 @@ bool types::isTextual(ID Id) {
case types::TY_RawSIL:
case types::TY_LLVM_IR:
case types::TY_ObjCHeader:
case types::TY_AutolinkFile:
return true;
case types::TY_Image:
case types::TY_Object:

View File

@@ -0,0 +1,6 @@
// RUN: %target-swiftc_driver -c %s -o %t
// RUN: %target-swift-autolink-extract %t -o - | FileCheck --check-prefix CHECK-%target-object-format %s
// REQUIRES: autolink-extract
// CHECK-elf: -lswiftCore

View File

@@ -0,0 +1,6 @@
// RUN: %target-swiftc_driver -c %s -o %t
// RUN: not %target-swift-autolink-extract 2>&1 | FileCheck %s
// REQUIRES: autolink-extract
// CHECK: <unknown>:0: error: this mode requires at least one input file

View File

@@ -0,0 +1,12 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: %target-swiftc_driver -emit-library -emit-module -emit-module-path %t/empty.swiftmodule -module-name empty -module-link-name empty %S/empty.swift
// RUN: %target-swiftc_driver -c %s -I %t -o %t/import_experimental.o
// RUN: %target-swift-autolink-extract %t/import_experimental.o -o - | FileCheck --check-prefix CHECK-%target-object-format %s
// REQUIRES: autolink-extract
// CHECK-elf-DAG: -lswiftCore
// CHECK-elf-DAG: -lempty
import empty

View File

@@ -0,0 +1,5 @@
// RUN: %target-swiftc_driver -### %s | FileCheck %s
// REQUIRES: autolink-extract
// CHECK: swift-autolink-extract {{.+}}.o -o {{.+}}.autolink

View File

@@ -1,9 +1,14 @@
// RUN: %swift_driver_plain --driver-mode=swiftc -driver-print-actions %s 2>&1 | FileCheck -check-prefix=CHECK-SWIFTC %s
// RUN: %swift_driver_plain --driver-mode=swiftc -driver-print-actions %s 2>&1 | FileCheck -check-prefix=CHECK-SWIFTC-%target-object-format %s
// RUN: %swift_driver_plain -driver-print-actions %s --driver-mode=swiftc 2>&1 | FileCheck -check-prefix=CHECK-SWIFT %s
// CHECK-SWIFTC: 0: input, "{{.*}}driver_mode.swift", swift
// CHECK-SWIFTC: 1: compile, {0}, object
// CHECK-SWIFTC: 2: link, {1}, image
// CHECK-SWIFTC-macho: 0: input, "{{.*}}driver_mode.swift", swift
// CHECK-SWIFTC-macho: 1: compile, {0}, object
// CHECK-SWIFTC-macho: 2: link, {1}, image
// CHECK-SWIFTC-elf: 0: input, "{{.*}}driver_mode.swift", swift
// CHECK-SWIFTC-elf: 1: compile, {0}, object
// CHECK-SWIFTC-elf: 2: swift-autolink-extract, {1}, autolink
// CHECK-SWIFTC-elf: 3: link, {1, 2}, image
// CHECK-SWIFT: 0: input, "{{.*}}driver_mode.swift", swift
// CHECK-SWIFT: 1: compile, {0}, none

View File

@@ -99,6 +99,7 @@
// DEBUG: -o linker.dSYM
// DEBUG_LINUX: bin/swift
// DEBUG_LINUX-NEXT: bin/swift-autolink-extract
// DEBUG_LINUX-NEXT: bin/swift
// DEBUG_LINUX-NEXT: bin/clang++{{"? }}
// DEBUG_LINUX: -o linker

View File

@@ -10,14 +10,18 @@
// COMPILE: 0: input
// COMPILE: 1: compile, {0}, object
// RUN: %swiftc_driver -driver-print-actions %t/empty 2>&1 | FileCheck -check-prefix=LINK %s
// RUN: %swiftc_driver -driver-print-actions %t/empty.swiftmodule 2>&1 | FileCheck -check-prefix=LINK %s
// RUN: %swiftc_driver -driver-print-actions %t/empty.o 2>&1 | FileCheck -check-prefix=LINK %s
// RUN: %swiftc_driver -driver-print-actions %t/empty 2>&1 | FileCheck -check-prefix=LINK-%target-object-format %s
// RUN: %swiftc_driver -driver-print-actions %t/empty.swiftmodule 2>&1 | FileCheck -check-prefix=LINK-%target-object-format %s
// RUN: %swiftc_driver -driver-print-actions %t/empty.o 2>&1 | FileCheck -check-prefix=LINK-%target-object-format %s
// RUN: not %swiftc_driver -driver-print-actions %t/empty.h 2>&1 | FileCheck -check-prefix=ERROR %s
// RUN: %swiftc_driver -driver-print-actions %t/empty.swift 2>&1 | FileCheck -check-prefix=COMPILE %s
// LINK: 0: input
// LINK: 1: link, {0}, image
// LINK-macho: 0: input
// LINK-macho: 1: link, {0}, image
// LINK-elf: 0: input
// LINK-elf: 1: swift-autolink-extract, {0}, autolink
// LINK-elf: 2: link, {0, 1}, image
// RUN: not %swiftc_driver -driver-print-actions -emit-module %t/empty 2>&1 | FileCheck -check-prefix=ERROR %s
// RUN: %swiftc_driver -driver-print-actions -emit-module %t/empty.swiftmodule 2>&1 | FileCheck -check-prefix=MODULE %s

View File

@@ -1,5 +1,6 @@
add_swift_executable(swift
driver.cpp
autolink_extract_main.cpp
frontend_main.cpp
update_main.cpp
LINK_LIBRARIES
@@ -25,6 +26,10 @@ add_custom_command(TARGET swift POST_BUILD
COMMAND "${CMAKE_COMMAND}" "-E" "create_symlink" "swift" "swift-update"
WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}")
add_custom_command(TARGET swift POST_BUILD
COMMAND "${CMAKE_COMMAND}" "-E" "create_symlink" "swift" "swift-autolink-extract"
WORKING_DIRECTORY "${SWIFT_RUNTIME_OUTPUT_INTDIR}")
# If building as part of clang, make sure the headers are installed.
if(NOT SWIFT_BUILT_STANDALONE)
add_dependencies(swift clang-headers)
@@ -56,3 +61,10 @@ swift_install_in_component(compiler
swift_install_in_component(compiler
FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-update"
DESTINATION "bin")
if(SWIFT_HOST_VARIANT STREQUAL "LINUX")
# No need for this on other platforms.
swift_install_in_component(compiler
FILES "${SWIFT_RUNTIME_OUTPUT_INTDIR}/swift-autolink-extract"
DESTINATION "bin")
endif()

View File

@@ -0,0 +1,182 @@
//===-- autolink_extract_main.cpp - autolink extraction utility -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Extracts autolink flags from object files so they can be passed to the
// linker directly. Mostly useful for platforms where the linker doesn't
// natively support autolinking (ie. ELF-based platforms).
//
//===----------------------------------------------------------------------===//
#include <set>
#include <string>
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/Option/Options.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/ELFObjectFile.h"
using namespace swift;
using namespace llvm::opt;
class AutolinkExtractInvocation {
private:
std::string MainExecutablePath;
std::string OutputFilename = "-";
std::vector<std::string> InputFilenames;
public:
void setMainExecutablePath(const std::string &Path) {
MainExecutablePath = Path;
}
const std::string &getOutputFilename() {
return OutputFilename;
}
const std::vector<std::string> &getInputFilenames() {
return InputFilenames;
}
int parseArgs(ArrayRef<const char *> Args,
DiagnosticEngine &Diags) {
using namespace options;
// Parse frontend command line options using Swift's option table.
std::unique_ptr<llvm::opt::InputArgList> ParsedArgs;
std::unique_ptr<llvm::opt::OptTable> Table = createSwiftOptTable();
unsigned MissingIndex;
unsigned MissingCount;
ParsedArgs.reset(
Table->ParseArgs(Args.begin(), Args.end(), MissingIndex, MissingCount,
AutolinkExtractOption));
if (MissingCount) {
Diags.diagnose(SourceLoc(), diag::error_missing_arg_value,
ParsedArgs->getArgString(MissingIndex), MissingCount);
return 1;
}
if (ParsedArgs->hasArg(OPT_UNKNOWN)) {
for (const Arg *A : make_range(ParsedArgs->filtered_begin(OPT_UNKNOWN),
ParsedArgs->filtered_end())) {
Diags.diagnose(SourceLoc(), diag::error_unknown_arg,
A->getAsString(*ParsedArgs));
}
return true;
}
if (ParsedArgs->getLastArg(OPT_help)) {
std::string ExecutableName = llvm::sys::path::stem(MainExecutablePath);
Table->PrintHelp(llvm::outs(), ExecutableName.c_str(),
"Swift Autolink Extract", options::AutolinkExtractOption,
0);
return 1;
}
for (const Arg *A : make_range(ParsedArgs->filtered_begin(OPT_INPUT),
ParsedArgs->filtered_end())) {
InputFilenames.push_back(A->getValue());
}
if (InputFilenames.empty()) {
Diags.diagnose(SourceLoc(), diag::error_mode_requires_an_input_file);
return 1;
}
if (const Arg *A = ParsedArgs->getLastArg(OPT_o)) {
OutputFilename = A->getValue();
}
return 0;
}
};
int autolink_extract_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr) {
CompilerInstance Instance;
PrintingDiagnosticConsumer PDC;
Instance.addDiagnosticConsumer(&PDC);
AutolinkExtractInvocation Invocation;
std::string MainExecutablePath = llvm::sys::fs::getMainExecutable(Argv0,
MainAddr);
Invocation.setMainExecutablePath(MainExecutablePath);
// Parse arguments.
if (Invocation.parseArgs(Args, Instance.getDiags()) != 0) {
return 1;
}
// Store each linker flag only once
std::set<std::string> LinkerFlags;
for (const auto &ObjectFileName : Invocation.getInputFilenames()) {
auto ObjectFileOwner =
llvm::object::ObjectFile::createObjectFile(ObjectFileName);
if (!ObjectFileOwner) {
Instance.getDiags().diagnose(SourceLoc(), diag::error_open_input_file,
ObjectFileName,
ObjectFileOwner.getError().message());
return 1;
}
auto ObjectFile = ObjectFileOwner->getBinary();
if (llvm::isa<llvm::object::ELFObjectFileBase>(ObjectFile)) {
// Search for the section we hold autolink entries in
for (auto &Section : ObjectFileOwner->getBinary()->sections()) {
llvm::StringRef SectionName;
Section.getName(SectionName);
if (SectionName == ".swift1_autolink_entries") {
llvm::StringRef SectionData;
Section.getContents(SectionData);
// entries are null-terminated, so extract them and push them into
// the set.
llvm::SmallVector<llvm::StringRef, 4> SplitFlags;
SectionData.split(SplitFlags, llvm::StringRef("\0", 1),
-1, /*KeepEmpty=*/false);
for (const auto &Flag : SplitFlags) {
LinkerFlags.insert(Flag);
}
}
}
} else {
Instance.getDiags().diagnose(SourceLoc(), diag::error_open_input_file,
ObjectFileName,
"Don't know how to extract from object file"
"format");
return 1;
}
}
std::string OutputFilename = Invocation.getOutputFilename();
std::error_code EC;
llvm::raw_fd_ostream OutOS(OutputFilename, EC, llvm::sys::fs::F_None);
if (OutOS.has_error() || EC) {
Instance.getDiags().diagnose(SourceLoc(), diag::error_opening_output,
OutputFilename, EC.message());
OutOS.clear_error();
return 1;
}
for (auto &Flag : LinkerFlags) {
OutOS << Flag << '\n';
}
return 0;
}

View File

@@ -51,6 +51,10 @@ extern int frontend_main(ArrayRef<const char *> Args, const char *Argv0,
extern int update_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
/// Run 'swift-autolink-extract'.
extern int autolink_extract_main(ArrayRef<const char *> Args, const char *Argv0,
void *MainAddr);
int main(int argc_, const char **argv_) {
// Print a stack trace if we signal out.
llvm::sys::PrintStackTraceOnErrorSignal();
@@ -87,10 +91,17 @@ int main(int argc_, const char **argv_) {
Diags.addConsumer(PDC);
Driver TheDriver(Path, llvm::sys::path::stem(argv[0]), argv, Diags);
if (TheDriver.getDriverKind() == Driver::DriverKind::UpdateCode) {
switch (TheDriver.getDriverKind()) {
case Driver::DriverKind::UpdateCode:
return update_main(TheDriver.getArgsWithoutProgramNameAndDriverMode(argv),
argv[0],
(void *)(intptr_t)getExecutablePath);
case Driver::DriverKind::AutolinkExtract:
return autolink_extract_main(
TheDriver.getArgsWithoutProgramNameAndDriverMode(argv),
argv[0], (void *)(intptr_t)getExecutablePath);
default:
break;
}
llvm::InitializeAllTargets();