mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:40:10 +01:00
Add subcommands and busybox entry points for LLVM tools (#5049)
This adds support for most of the remaining LLVM command line tools using a generic, generated wrapper. The subcommand interface for these is (much) less interesting than our other subcommands, but it gives us a uniform and consistent layer. Note that I structured these as nested sub-sub-commands below an `llvm` subcommand because of an expectation that we will want to add more, and ones that don't use this generic layer. Some concrete future work: - Add the `opt` and `llc` tools as subcommands for easier debugging and experimentation with LLVM IR output from Carbon's toolchain. - Potentially sink `lld` below the `llvm` layer given that it has significantly less user visibility than commands like `clang`. Unfortunately, the current driver subcommand APIs make nested subcommands awkward. I've added a somewhat rough hack here to let the LLVM tools go in, but there are some TODOs that I want to address in a follow-up that works to adjust the structure of this code to be more conducive to nesting like this. Depends on #5048 -- only the last commit should be reviewed here. --------- Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
co-authored by
Geoff Romer
parent
c2281d1250
commit
1459332031
@@ -68,7 +68,9 @@ EXTERNAL_REPOS: dict[str, ExternalRepo] = {
|
||||
}
|
||||
|
||||
IGNORE_SOURCE_FILE_REGEX = re.compile(
|
||||
r"^(third_party/clangd.*|common/version.*\.cpp|.*_autogen_manifest\.cpp)$"
|
||||
r"^(third_party/clangd.*|common/version.*\.cpp"
|
||||
r"|.*_autogen_manifest\.cpp"
|
||||
r"|toolchain/base/llvm_tools.def)$"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
load("llvm_tools.bzl", "LLVM_MAIN_TOOLS", "generate_llvm_tools_def")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
@@ -120,6 +121,37 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
generate_llvm_tools_def(
|
||||
name = "llvm_tools_def",
|
||||
out = "llvm_tools.def",
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "is_macos",
|
||||
constraint_values = ["@platforms//os:macos"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "llvm_tools",
|
||||
srcs = ["llvm_tools.cpp"],
|
||||
hdrs = ["llvm_tools.h"],
|
||||
linkopts = select({
|
||||
# TODO: This should be moved upstream to LLVM's tool libraries that
|
||||
# require it.
|
||||
":is_macos": [
|
||||
"-framework",
|
||||
"CoreFoundation",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
deps = [
|
||||
":llvm_tools_def",
|
||||
"//common:command_line",
|
||||
"//common:enum_base",
|
||||
"@llvm-project//llvm:Support",
|
||||
] + [info.lib for info in LLVM_MAIN_TOOLS.values()],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "shared_value_stores",
|
||||
hdrs = ["shared_value_stores.h"],
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
# Exceptions. See /LICENSE for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
"""Provides variables and rules to automate working with LLVM's CLI tools."""
|
||||
|
||||
load("@rules_cc//cc:cc_library.bzl", "cc_library")
|
||||
|
||||
# The main LLVM command line tools, including their "primary" name, binary name,
|
||||
# and the library dependency required to use them.
|
||||
LLVM_MAIN_TOOLS = {
|
||||
"ar": struct(bin_name = "llvm-ar", lib = "@llvm-project//llvm:llvm-ar-lib"),
|
||||
"cgdata": struct(bin_name = "llvm-cgdata", lib = "@llvm-project//llvm:llvm-cgdata-lib"),
|
||||
"cxxfilt": struct(bin_name = "llvm-cxxfilt", lib = "@llvm-project//llvm:llvm-cxxfilt-lib"),
|
||||
"debuginfod-find": struct(bin_name = "llvm-debuginfod-find", lib = "@llvm-project//llvm:llvm-debuginfod-find-lib"),
|
||||
"dsymutil": struct(bin_name = "dsymutil", lib = "@llvm-project//llvm:dsymutil-lib"),
|
||||
"dwp": struct(bin_name = "llvm-dwp", lib = "@llvm-project//llvm:llvm-dwp-lib"),
|
||||
"gsymutil": struct(bin_name = "llvm-gsymutil", lib = "@llvm-project//llvm:llvm-gsymutil-lib"),
|
||||
"ifs": struct(bin_name = "llvm-ifs", lib = "@llvm-project//llvm:llvm-ifs-lib"),
|
||||
"libtool-darwin": struct(bin_name = "llvm-libtool-darwin", lib = "@llvm-project//llvm:llvm-libtool-darwin-lib"),
|
||||
"lipo": struct(bin_name = "llvm-lipo", lib = "@llvm-project//llvm:llvm-lipo-lib"),
|
||||
"ml": struct(bin_name = "llvm-ml", lib = "@llvm-project//llvm:llvm-ml-lib"),
|
||||
"mt": struct(bin_name = "llvm-mt", lib = "@llvm-project//llvm:llvm-mt-lib"),
|
||||
"nm": struct(bin_name = "llvm-nm", lib = "@llvm-project//llvm:llvm-nm-lib"),
|
||||
"objcopy": struct(bin_name = "llvm-objcopy", lib = "@llvm-project//llvm:llvm-objcopy-lib"),
|
||||
"objdump": struct(bin_name = "llvm-objdump", lib = "@llvm-project//llvm:llvm-objdump-lib"),
|
||||
"profdata": struct(bin_name = "llvm-profdata", lib = "@llvm-project//llvm:llvm-profdata-lib"),
|
||||
"rc": struct(bin_name = "llvm-rc", lib = "@llvm-project//llvm:llvm-rc-lib"),
|
||||
"readobj": struct(bin_name = "llvm-readobj", lib = "@llvm-project//llvm:llvm-readobj-lib"),
|
||||
"sancov": struct(bin_name = "sancov", lib = "@llvm-project//llvm:sancov-lib"),
|
||||
"size": struct(bin_name = "llvm-size", lib = "@llvm-project//llvm:llvm-size-lib"),
|
||||
"symbolizer": struct(bin_name = "llvm-symbolizer", lib = "@llvm-project//llvm:llvm-symbolizer-lib"),
|
||||
}
|
||||
|
||||
# A collection of additional alias names that should be available for the main
|
||||
# tools. The key is the main tool with the support for these names, followed by
|
||||
# a list of the aliased names.
|
||||
#
|
||||
# Note that we don't track separate binary names for the alias names as those
|
||||
# are always formed by prepending `llvm-` for the aliases.
|
||||
LLVM_TOOL_ALIASES = {
|
||||
"ar": ["ranlib", "lib", "dlltool"],
|
||||
"objcopy": ["bitcode-strip", "install-name-tool", "strip"],
|
||||
"objdump": ["otool"],
|
||||
"rc": ["windres"],
|
||||
"readobj": ["readelf"],
|
||||
"symbolizer": ["addr2line"],
|
||||
}
|
||||
|
||||
_DEF_FILE_TEMPLATE = """
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// This is a generated X-macro header for defining LLVM tools. It does not use
|
||||
// `#include` guards, and instead is designed to be `#include`ed after the
|
||||
// x-macro is defined in order for its inclusion to expand to the desired
|
||||
// output. Macro definitions are cleaned up at the end of this file.
|
||||
//
|
||||
// Each X-macro takes four arguments:
|
||||
// - `Id` is the identifier-shaped PascalCased tool name.
|
||||
// - `Name` is a string literal of the tool name.
|
||||
// - `BinName` is a string literal of the binary name of the tool when installed
|
||||
// as a stand-alone command line tool.
|
||||
// - `MainFn` is the function symbol name used to run the tool as-if its `main`.
|
||||
//
|
||||
// There are three X-macros available:
|
||||
// - `CARBON_LLVM_TOOL` is available for every tool.
|
||||
// - `CARBON_LLVM_MAIN_TOOL` is available for each tool with a distinct
|
||||
// `MainFn` symbol name.
|
||||
// - `CARBON_LLVM_ALIAS_TOOL` is available for each tool that is an alias of
|
||||
// some other tool. It's `MainFn` will be the alias-target symbol name.
|
||||
//
|
||||
// See toolchain/driver/llvm_tools.bzl for more details.
|
||||
|
||||
#ifndef CARBON_LLVM_TOOL
|
||||
#define CARBON_LLVM_TOOL(Id, Name, BinName, MainFn)
|
||||
#endif
|
||||
|
||||
#ifndef CARBON_LLVM_MAIN_TOOL
|
||||
#define CARBON_LLVM_MAIN_TOOL(Id, Name, BinName, MainFn) \\
|
||||
CARBON_LLVM_TOOL(Id, Name, BinName, MainFn)
|
||||
#endif
|
||||
|
||||
#ifndef CARBON_LLVM_ALIAS_TOOL
|
||||
#define CARBON_LLVM_ALIAS_TOOL(Id, Name, BinName, MainFn) \\
|
||||
CARBON_LLVM_TOOL(Id, Name, BinName, MainFn)
|
||||
#endif
|
||||
|
||||
{}
|
||||
|
||||
#undef CARBON_LLVM_TOOL
|
||||
#undef CARBON_LLVM_MAIN_TOOL
|
||||
#undef CARBON_LLVM_ALIAS_TOOL
|
||||
"""
|
||||
|
||||
_DEF_MACRO_TEMPLATE = """
|
||||
CARBON_LLVM_{kind}TOOL({id}, "{name}", "{bin_name}", {main_fn})
|
||||
""".strip()
|
||||
|
||||
def _build_def_macro(kind, name, bin_name, main_info):
|
||||
id = "".join([w.title() for w in name.split("-")])
|
||||
main_fn = main_info.bin_name.replace("-", "_") + "_main"
|
||||
return _DEF_MACRO_TEMPLATE.format(
|
||||
kind = kind,
|
||||
id = id,
|
||||
name = name,
|
||||
bin_name = bin_name,
|
||||
main_fn = main_fn,
|
||||
)
|
||||
|
||||
def _generate_llvm_tools_def_rule(ctx):
|
||||
def_lines = []
|
||||
|
||||
for name, tool_info in LLVM_MAIN_TOOLS.items():
|
||||
def_lines.append(_build_def_macro("MAIN_", name, tool_info.bin_name, tool_info))
|
||||
|
||||
for target, aliases in LLVM_TOOL_ALIASES.items():
|
||||
tool_info = LLVM_MAIN_TOOLS[target]
|
||||
for alias in aliases:
|
||||
bin_name = "llvm-" + alias
|
||||
def_lines.append(_build_def_macro("ALIAS_", alias, bin_name, tool_info))
|
||||
|
||||
def_file = ctx.actions.declare_file(ctx.label.name)
|
||||
ctx.actions.write(def_file, _DEF_FILE_TEMPLATE.format("\n".join(def_lines)))
|
||||
return [DefaultInfo(files = depset([def_file]))]
|
||||
|
||||
generate_llvm_tools_def_rule = rule(
|
||||
implementation = _generate_llvm_tools_def_rule,
|
||||
attrs = {},
|
||||
)
|
||||
|
||||
def generate_llvm_tools_def(name, out, **kwargs):
|
||||
"""Generates the LLVM tools `.def` file.
|
||||
|
||||
This first generates the `.def` file into the `out` filename, and then
|
||||
synthesizes a `cc_library` rule exporting that file in its `textual_hdrs`.
|
||||
|
||||
The `cc_library` rule name is the provided `name` and should be depended on
|
||||
by code that includes the generated file. The `kwargs` are expanded into the
|
||||
`cc_library` in case other attributes need to be configured there.
|
||||
|
||||
The two-step process is necessary to avoid trying to compile or otherwise
|
||||
process the generated file as something other than a textual header.
|
||||
"""
|
||||
generate_llvm_tools_def_rule(name = out)
|
||||
cc_library(
|
||||
name = name,
|
||||
textual_hdrs = [out],
|
||||
**kwargs
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/base/llvm_tools.h"
|
||||
|
||||
#include "common/command_line.h"
|
||||
|
||||
// NOLINTBEGIN(readability-identifier-naming): External library name.
|
||||
#define CARBON_LLVM_MAIN_TOOL(Identifier, Name, BinName, MainFn) \
|
||||
extern auto MainFn(int argc, char** argv, const llvm::ToolContext& context) \
|
||||
-> int;
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
// NOLINTEND(readability-identifier-naming): External library name.
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
CARBON_DEFINE_ENUM_CLASS_NAMES(LLVMTool) = {
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) Name,
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
};
|
||||
|
||||
constexpr llvm::StringLiteral LLVMTool::BinNames[] = {
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) BinName,
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
};
|
||||
|
||||
constexpr LLVMTool::MainFnT* LLVMTool::MainFns[] = {
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) &::MainFn,
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
};
|
||||
|
||||
constexpr CommandLine::CommandInfo LLVMTool::SubcommandInfos[] = {
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \
|
||||
{.name = Name, \
|
||||
.help = "Runs the LLVM " Name \
|
||||
" command line tool with the provided arguments."},
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,75 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_BASE_LLVM_TOOLS_H_
|
||||
#define CARBON_TOOLCHAIN_BASE_LLVM_TOOLS_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "common/enum_base.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/LLVMDriver.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
CARBON_DEFINE_RAW_ENUM_CLASS(LLVMTool, uint8_t) {
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \
|
||||
CARBON_RAW_ENUM_ENUMERATOR(Identifier)
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
};
|
||||
|
||||
// An enum-like class for each of the LLVM tools.
|
||||
//
|
||||
// This can be used like an enum to track a specific one of the LLVM tools. It
|
||||
// also has a collection of methods to access various aspects of the tools
|
||||
// themselves, including the symbol used to invoke the given tool.
|
||||
//
|
||||
// The instances of this class are generated from `llvm_tools.bzl`, see that
|
||||
// file for more details.
|
||||
class LLVMTool : public CARBON_ENUM_BASE(LLVMTool) {
|
||||
public:
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \
|
||||
CARBON_ENUM_CONSTANT_DECL(Identifier)
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
|
||||
static const llvm::ArrayRef<LLVMTool> Tools;
|
||||
|
||||
using MainFnT = auto(int argc, char** argv, const llvm::ToolContext& context)
|
||||
-> int;
|
||||
|
||||
using EnumBase::EnumBase;
|
||||
|
||||
auto bin_name() const -> llvm::StringLiteral { return BinNames[AsInt()]; }
|
||||
|
||||
auto main_fn() const -> MainFnT* { return MainFns[AsInt()]; }
|
||||
|
||||
auto subcommand_info() const -> const CommandLine::CommandInfo& {
|
||||
return SubcommandInfos[AsInt()];
|
||||
}
|
||||
|
||||
private:
|
||||
static const LLVMTool ToolsStorage[];
|
||||
|
||||
static const llvm::StringLiteral BinNames[];
|
||||
static MainFnT* const MainFns[];
|
||||
|
||||
static const CommandLine::CommandInfo SubcommandInfos[];
|
||||
};
|
||||
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \
|
||||
CARBON_ENUM_CONSTANT_DEFINITION(LLVMTool, Identifier)
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
|
||||
constexpr LLVMTool LLVMTool::ToolsStorage[] = {
|
||||
#define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \
|
||||
LLVMTool::Identifier,
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
};
|
||||
constexpr llvm::ArrayRef<LLVMTool> LLVMTool::Tools = ToolsStorage;
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_BASE_LLVM_TOOLS_H_
|
||||
@@ -104,6 +104,8 @@ cc_library(
|
||||
"link_subcommand.h",
|
||||
"lld_subcommand.cpp",
|
||||
"lld_subcommand.h",
|
||||
"llvm_subcommand.cpp",
|
||||
"llvm_subcommand.h",
|
||||
],
|
||||
hdrs = [
|
||||
"driver.h",
|
||||
@@ -116,12 +118,14 @@ cc_library(
|
||||
deps = [
|
||||
":clang_runner",
|
||||
":lld_runner",
|
||||
":llvm_runner",
|
||||
"//common:command_line",
|
||||
"//common:error",
|
||||
"//common:ostream",
|
||||
"//common:raw_string_ostream",
|
||||
"//common:version",
|
||||
"//common:vlog",
|
||||
"//toolchain/base:llvm_tools",
|
||||
"//toolchain/base:pretty_stack_trace_function",
|
||||
"//toolchain/base:shared_value_stores",
|
||||
"//toolchain/base:timings",
|
||||
@@ -223,6 +227,41 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "llvm_runner",
|
||||
srcs = ["llvm_runner.cpp"],
|
||||
hdrs = ["llvm_runner.h"],
|
||||
deps = [
|
||||
":tool_runner_base",
|
||||
"//common:ostream",
|
||||
"//common:vlog",
|
||||
"//toolchain/base:llvm_tools",
|
||||
"//toolchain/install:install_paths",
|
||||
"@llvm-project//lld:Common",
|
||||
"@llvm-project//lld:ELF",
|
||||
"@llvm-project//lld:MachO",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "llvm_runner_test",
|
||||
size = "small",
|
||||
srcs = ["llvm_runner_test.cpp"],
|
||||
env = cc_env(),
|
||||
deps = [
|
||||
":llvm_runner",
|
||||
"//common:all_llvm_targets",
|
||||
"//common:ostream",
|
||||
"//common:raw_string_ostream",
|
||||
"//testing/base:capture_std_streams",
|
||||
"//testing/base:global_exe_path",
|
||||
"//testing/base:gtest_main",
|
||||
"@googletest//:gtest",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "tool_runner_base",
|
||||
srcs = ["tool_runner_base.cpp"],
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "toolchain/driver/language_server_subcommand.h"
|
||||
#include "toolchain/driver/link_subcommand.h"
|
||||
#include "toolchain/driver/lld_subcommand.h"
|
||||
#include "toolchain/driver/llvm_subcommand.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -35,6 +36,7 @@ struct Options {
|
||||
LanguageServerSubcommand language_server;
|
||||
LinkSubcommand link;
|
||||
LldSubcommand lld;
|
||||
LLVMSubcommand llvm;
|
||||
|
||||
// On success, this is set to the subcommand to run.
|
||||
DriverSubcommand* selected_subcommand = nullptr;
|
||||
@@ -92,6 +94,7 @@ applies to each message that forms a diagnostic, not just the primary message.
|
||||
language_server.AddTo(b, &selected_subcommand);
|
||||
link.AddTo(b, &selected_subcommand);
|
||||
lld.AddTo(b, &selected_subcommand);
|
||||
llvm.AddTo(b, &selected_subcommand);
|
||||
|
||||
b.RequiresSubcommand();
|
||||
}
|
||||
|
||||
@@ -32,16 +32,31 @@ class DriverSubcommand {
|
||||
// the subcommand is in use.
|
||||
auto AddTo(CommandLine::CommandBuilder& b,
|
||||
DriverSubcommand** selected_subcommand) -> void {
|
||||
b.AddSubcommand(info_, [this, selected_subcommand](
|
||||
CommandLine::CommandBuilder& sub_b) {
|
||||
BuildOptions(sub_b);
|
||||
sub_b.Do([this, selected_subcommand] { *selected_subcommand = this; });
|
||||
});
|
||||
b.AddSubcommand(
|
||||
info_, [this, selected_subcommand](CommandLine::CommandBuilder& sub_b) {
|
||||
BuildOptionsAndSetAction(sub_b, selected_subcommand);
|
||||
});
|
||||
}
|
||||
|
||||
// Adds command line options.
|
||||
virtual auto BuildOptions(CommandLine::CommandBuilder& b) -> void = 0;
|
||||
|
||||
// Adds command line options and registers the `Run` method to be called.
|
||||
//
|
||||
// For more complex subcommands that need to control the action this method
|
||||
// can be overridden and bypass the simple API above.
|
||||
//
|
||||
// TODO: This isn't the most elegant way to manage this. There is probably a
|
||||
// better factoring / organization of the driver subcommand infrastructure
|
||||
// that bakes in the needed flexibility here, but that was deferred for a
|
||||
// future refactoring.
|
||||
virtual auto BuildOptionsAndSetAction(CommandLine::CommandBuilder& b,
|
||||
DriverSubcommand** selected_subcommand)
|
||||
-> void {
|
||||
BuildOptions(b);
|
||||
b.Do([this, selected_subcommand] { *selected_subcommand = this; });
|
||||
}
|
||||
|
||||
// Runs the command.
|
||||
virtual auto Run(DriverEnv& driver_env) -> DriverResult = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/driver/llvm_runner.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
|
||||
#include "common/vlog.h"
|
||||
#include "lld/Common/Driver.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
auto LLVMRunner::Run(LLVMTool tool, llvm::ArrayRef<llvm::StringRef> args)
|
||||
-> bool {
|
||||
std::string path = installation_->llvm_tool_path(tool);
|
||||
|
||||
// Allocate one chunk of storage for the actual C-strings and a vector of
|
||||
// pointers into the storage.
|
||||
llvm::OwningArrayRef<char> cstr_arg_storage;
|
||||
llvm::SmallVector<const char*, 64> cstr_args = BuildCStrArgs(
|
||||
tool.name(), path, /*verbose_flag=*/std::nullopt, args, cstr_arg_storage);
|
||||
|
||||
CARBON_VLOG("Running LLVM's {0} tool...\n", tool.name());
|
||||
int exit_code = tool.main_fn()(
|
||||
cstr_args.size(), const_cast<char**>(cstr_args.data()),
|
||||
{.Path = path.c_str(), .PrependArg = nullptr, .NeedsPrependArg = false});
|
||||
|
||||
// TODO: Should this be forwarding the full exit code?
|
||||
return exit_code == 0;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,26 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_DRIVER_LLVM_RUNNER_H_
|
||||
#define CARBON_TOOLCHAIN_DRIVER_LLVM_RUNNER_H_
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "toolchain/base/llvm_tools.h"
|
||||
#include "toolchain/driver/tool_runner_base.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Runs any of the LLVM tools in a manner similar to invoking it with the
|
||||
// provided arguments.
|
||||
class LLVMRunner : ToolRunnerBase {
|
||||
public:
|
||||
using ToolRunnerBase::ToolRunnerBase;
|
||||
|
||||
auto Run(LLVMTool tool, llvm::ArrayRef<llvm::StringRef> args) -> bool;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_DRIVER_LLVM_RUNNER_H_
|
||||
@@ -0,0 +1,96 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/driver/llvm_runner.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "common/raw_string_ostream.h"
|
||||
#include "llvm/ADT/ScopeExit.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "testing/base/capture_std_streams.h"
|
||||
#include "testing/base/global_exe_path.h"
|
||||
|
||||
namespace Carbon {
|
||||
namespace {
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::StrEq;
|
||||
|
||||
TEST(LLVMRunnerTest, Version) {
|
||||
RawStringOstream test_os;
|
||||
const auto install_paths =
|
||||
InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
|
||||
LLVMRunner runner(&install_paths, &test_os);
|
||||
|
||||
std::string out;
|
||||
std::string err;
|
||||
|
||||
for (LLVMTool tool : LLVMTool::Tools) {
|
||||
std::string test_flag = "--version";
|
||||
std::string expected_out = "LLVM version";
|
||||
|
||||
// Handle any special requirements for specific tools.
|
||||
switch (tool) {
|
||||
case LLVMTool::Addr2Line:
|
||||
case LLVMTool::BitcodeStrip:
|
||||
case LLVMTool::Cgdata:
|
||||
case LLVMTool::DebuginfodFind:
|
||||
case LLVMTool::Dwp:
|
||||
case LLVMTool::Gsymutil:
|
||||
case LLVMTool::Ifs:
|
||||
case LLVMTool::InstallNameTool:
|
||||
case LLVMTool::Lipo:
|
||||
case LLVMTool::Objcopy:
|
||||
case LLVMTool::Profdata:
|
||||
case LLVMTool::Sancov:
|
||||
case LLVMTool::Strip:
|
||||
case LLVMTool::Symbolizer:
|
||||
case LLVMTool::Windres:
|
||||
// TODO: These tools are not well behaved when invoked as a library,
|
||||
// typically directly calling `exit` on some or all code paths. We
|
||||
// should see if they can be fixed upstream and re-enable testing.
|
||||
continue;
|
||||
|
||||
case LLVMTool::Dlltool:
|
||||
case LLVMTool::Rc:
|
||||
// No good flags to generically test these tools.
|
||||
continue;
|
||||
|
||||
case LLVMTool::Lib:
|
||||
test_flag = "/help";
|
||||
expected_out = "LLVM Lib";
|
||||
break;
|
||||
|
||||
case LLVMTool::Ml:
|
||||
test_flag = "/help";
|
||||
expected_out = "LLVM MASM Assembler";
|
||||
break;
|
||||
|
||||
case LLVMTool::Mt:
|
||||
test_flag = "/help";
|
||||
expected_out = "Manifest Tool";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(Testing::CallWithCapturedOutput(
|
||||
out, err, [&] { return runner.Run(tool, {test_flag}); }));
|
||||
|
||||
// The arguments to the LLVM tool should be part of the verbose log.
|
||||
EXPECT_THAT(test_os.TakeStr(), HasSubstr(test_flag));
|
||||
|
||||
// Nothing should print to stderr here.
|
||||
EXPECT_THAT(err, StrEq(""));
|
||||
|
||||
EXPECT_THAT(out, HasSubstr(expected_out));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,73 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#include "toolchain/driver/llvm_subcommand.h"
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "llvm/TargetParser/Host.h"
|
||||
#include "llvm/TargetParser/Triple.h"
|
||||
#include "toolchain/base/llvm_tools.h"
|
||||
#include "toolchain/driver/llvm_runner.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
static constexpr CommandLine::CommandInfo SubcommandInfo = {
|
||||
.name = "llvm",
|
||||
.help = R"""(
|
||||
Runs LLVM's command line tools with the provided arguments.
|
||||
|
||||
This subcommand provides access to a collection of LLVM's command line tools via
|
||||
further subcommands. For each of these tools, their command line can be provided
|
||||
using positional arguments.
|
||||
)""",
|
||||
};
|
||||
|
||||
auto LLVMOptions::Build(CommandLine::CommandBuilder& b,
|
||||
DriverSubcommand* subcommand,
|
||||
DriverSubcommand** selected_subcommand) -> void {
|
||||
// Add further subcommands for each LLVM tool.
|
||||
for (LLVMTool tool : LLVMTool::Tools) {
|
||||
// TODO: The subcommand info for each tool is weirdly stored in the
|
||||
// `LLVMTool` class instead of here where it makes more logical sense.
|
||||
// Either we should figure out how to move it to here or we should more
|
||||
// fully document the oddity of having it in the `LLVMTool` class.
|
||||
//
|
||||
// TODO: Currently, the command line subsystem's help isn't as user friendly
|
||||
// for the generated subcommands below this as it could be. Because each of
|
||||
// these has a completely generic and stamped out info, the `help` output is
|
||||
// very repetitive and doesn't actually contribute much information.
|
||||
b.AddSubcommand(tool.subcommand_info(), [&](auto& sub_b) {
|
||||
sub_b.AddStringPositionalArg(
|
||||
{
|
||||
.name = "ARG",
|
||||
.help = R"""(
|
||||
Arguments passed to the LLVM tool.
|
||||
)""",
|
||||
},
|
||||
[&](auto& arg_b) { arg_b.Append(&args); });
|
||||
sub_b.Do([=, this] {
|
||||
subcommand_tool = tool;
|
||||
*selected_subcommand = subcommand;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// The `llvm` subcommand can't be used directly.
|
||||
b.RequiresSubcommand();
|
||||
}
|
||||
|
||||
LLVMSubcommand::LLVMSubcommand() : DriverSubcommand(SubcommandInfo) {}
|
||||
|
||||
auto LLVMSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
|
||||
LLVMRunner runner(driver_env.installation, driver_env.vlog_stream);
|
||||
|
||||
// Don't run arbitrary LLVM tools and libraries when fuzzing.
|
||||
if (!DisableFuzzingExternalLibraries(driver_env, "llvm")) {
|
||||
return {.success = false};
|
||||
}
|
||||
|
||||
return {.success = runner.Run(options_.subcommand_tool, options_.args)};
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,59 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_DRIVER_LLVM_SUBCOMMAND_H_
|
||||
#define CARBON_TOOLCHAIN_DRIVER_LLVM_SUBCOMMAND_H_
|
||||
|
||||
#include "common/command_line.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "toolchain/base/llvm_tools.h"
|
||||
#include "toolchain/driver/driver_env.h"
|
||||
#include "toolchain/driver/driver_subcommand.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Options for the LLVM subcommand.
|
||||
//
|
||||
// See the implementation of `Build` for documentation on members.
|
||||
struct LLVMOptions {
|
||||
// Build the LLVM subcommand options using `b`.
|
||||
//
|
||||
// When this top-level subcommand is selected (potentially through a nested
|
||||
// sub-subcommand), the `selected_subcommand` will be set to point to
|
||||
// `subcommand` to reflect that.
|
||||
auto Build(CommandLine::CommandBuilder& b, DriverSubcommand* subcommand,
|
||||
DriverSubcommand** selected_subcommand) -> void;
|
||||
|
||||
LLVMTool subcommand_tool;
|
||||
llvm::SmallVector<llvm::StringRef> args;
|
||||
};
|
||||
|
||||
// Implement the LLVM subcommand of the driver.
|
||||
//
|
||||
// This provides access to the full collection of LLVM command line tools.
|
||||
class LLVMSubcommand : public DriverSubcommand {
|
||||
public:
|
||||
explicit LLVMSubcommand();
|
||||
|
||||
// The LLVM subcommand uses a custom subcommand structure, so `BuildOptions`
|
||||
// is a no-op and we override the more complex layer.
|
||||
auto BuildOptions(CommandLine::CommandBuilder& /*b*/) -> void override {
|
||||
CARBON_FATAL("Unused.");
|
||||
}
|
||||
auto BuildOptionsAndSetAction(CommandLine::CommandBuilder& b,
|
||||
DriverSubcommand** selected_subcommand)
|
||||
-> void override {
|
||||
options_.Build(b, this, selected_subcommand);
|
||||
}
|
||||
|
||||
auto Run(DriverEnv& driver_env) -> DriverResult override;
|
||||
|
||||
private:
|
||||
LLVMOptions options_;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_DRIVER_LLVM_SUBCOMMAND_H_
|
||||
@@ -0,0 +1,15 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// ARGS: --include-diagnostic-kind --fuzzing llvm ar -- --version
|
||||
//
|
||||
// SET-CAPTURE-CONSOLE-OUTPUT
|
||||
// clang-format off
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/fail_llvm_fuzzing.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/fail_llvm_fuzzing.carbon
|
||||
// CHECK:STDERR: error: preventing fuzzing of `llvm` subcommand due to external library [ToolFuzzingDisallowed]
|
||||
// CHECK:STDERR:
|
||||
+13
-1
@@ -9,6 +9,7 @@ load(
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
|
||||
load("//bazel/cc_toolchains:defs.bzl", "cc_env")
|
||||
load("//bazel/manifest:defs.bzl", "manifest")
|
||||
load("//toolchain/base:llvm_tools.bzl", "LLVM_MAIN_TOOLS", "LLVM_TOOL_ALIASES")
|
||||
load("install_filegroups.bzl", "install_filegroup", "install_symlink", "install_target", "make_install_filegroups")
|
||||
load("pkg_helpers.bzl", "pkg_naming_variables", "pkg_tar_and_test")
|
||||
|
||||
@@ -30,6 +31,7 @@ cc_library(
|
||||
deps = [
|
||||
"//common:check",
|
||||
"//common:error",
|
||||
"//toolchain/base:llvm_tools",
|
||||
"@bazel_tools//tools/cpp/runfiles",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
@@ -111,6 +113,7 @@ cc_binary(
|
||||
"//common:exe_path",
|
||||
"//common:init_llvm",
|
||||
"//common:version_stamp",
|
||||
"//toolchain/base:llvm_tools_def",
|
||||
"//toolchain/driver",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
@@ -123,6 +126,15 @@ lld_aliases = [
|
||||
"ld64.lld",
|
||||
]
|
||||
|
||||
llvm_binaries = lld_aliases + [
|
||||
tool.bin_name
|
||||
for tool in LLVM_MAIN_TOOLS.values()
|
||||
] + [
|
||||
"llvm-" + alias
|
||||
for (_, aliases) in LLVM_TOOL_ALIASES.items()
|
||||
for alias in aliases
|
||||
]
|
||||
|
||||
filegroup(
|
||||
name = "clang_headers",
|
||||
srcs = ["@llvm-project//clang:builtin_headers_gen"],
|
||||
@@ -165,7 +177,7 @@ install_dirs = {
|
||||
name,
|
||||
"../../carbon-busybox",
|
||||
is_driver = True,
|
||||
) for name in lld_aliases],
|
||||
) for name in llvm_binaries],
|
||||
"lib/carbon/llvm/lib/clang/" + LLVM_VERSION_MAJOR: [
|
||||
install_filegroup("include", ":clang_headers", "staging/include/"),
|
||||
],
|
||||
|
||||
@@ -54,6 +54,15 @@ static auto Main(int argc, char** argv) -> ErrorOr<int> {
|
||||
*busybox_info.mode)
|
||||
.Case("ld.lld", {"lld", "--platform=gnu", "--"})
|
||||
.Case("ld64.lld", {"lld", "--platform=darwin", "--"})
|
||||
|
||||
// We also support a number of LLVM tools with a trivial translation
|
||||
// to subcommands. If any of these end up needing more advanced
|
||||
// translation, that can be factored into the `.def` file to provide custom
|
||||
// expansion here.
|
||||
#define CARBON_LLVM_TOOL(Id, Name, BinName, MainFn) \
|
||||
.Case(BinName, {"llvm", Name, "--"})
|
||||
#include "toolchain/base/llvm_tools.def"
|
||||
|
||||
.Default({*busybox_info.mode, "--"});
|
||||
args.append(subcommand_args);
|
||||
}
|
||||
|
||||
@@ -195,4 +195,12 @@ auto InstallPaths::ld64_lld_path() const -> std::string {
|
||||
return path.str().str();
|
||||
}
|
||||
|
||||
auto InstallPaths::llvm_tool_path(LLVMTool tool) const -> std::string {
|
||||
llvm::SmallString<256> path(prefix_);
|
||||
// TODO: Adjust this to work equally well on Windows.
|
||||
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
|
||||
"lib/carbon/llvm/bin", tool.bin_name());
|
||||
return path.str().str();
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "toolchain/base/llvm_tools.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -92,6 +93,9 @@ class InstallPaths {
|
||||
auto ld_lld_path() const -> std::string;
|
||||
auto ld64_lld_path() const -> std::string;
|
||||
|
||||
// The path to any of the LLVM tools.
|
||||
auto llvm_tool_path(LLVMTool tool) const -> std::string;
|
||||
|
||||
private:
|
||||
friend class InstallPathsTestPeer;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user