Skip to content

Commit 957599b

Browse files
authored
Implement a basic busybox for carbon/clang. (#4406)
For reference, we're going down the busyboxing route because Carbon depends on Clang, and we want both to be available as binaries. Busyboxing allows this while avoiding duplicating symbols between multiple binaries. I'm removing the `cc_binary` for `driver:carbon` because I want to avoid a significant increase in binary outputs; `bazel run //toolchain` still works great. This still doesn't have great test coverage (but non-zero: `//examples:sieve` still builds/runs, for example). The problem is that we want to avoid subprocessing for performance, but this mainly deals with subprocessing. I'm still thinking about good approaches for that, since we'll probably want more significant testing for `clang` interaction... the solution might involve busyboxing `file_test` too. Note development on this ran into the argv issue being fixed in #4405
1 parent 69d1d34 commit 957599b

File tree

11 files changed

+190
-82
lines changed

11 files changed

+190
-82
lines changed

bazel/check_deps/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ filegroup(
1818
"//language_server",
1919
"//migrate_cpp:rewriter",
2020
"//migrate_cpp/cpp_refactoring",
21-
"//toolchain/driver:carbon",
21+
"//toolchain/install:carbon-busybox",
2222
"//utils/treesitter",
2323
],
2424
tags = ["manual"],

toolchain/driver/BUILD

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
44

55
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
6-
load("//bazel/cc_toolchains:defs.bzl", "cc_env")
76
load("//testing/fuzzing:rules.bzl", "cc_fuzz_test")
87

98
package(default_visibility = ["//visibility:public"])
@@ -165,21 +164,3 @@ cc_fuzz_test(
165164
"@llvm-project//llvm:Support",
166165
],
167166
)
168-
169-
# This target doesn't include prelude libraries. To get a target that has the
170-
# prelude available, use //toolchain.
171-
cc_binary(
172-
name = "carbon",
173-
srcs = ["driver_main.cpp"],
174-
env = cc_env(),
175-
deps = [
176-
":driver",
177-
"//common:all_llvm_targets",
178-
"//common:bazel_working_dir",
179-
"//common:exe_path",
180-
"//common:init_llvm",
181-
"//common:version_stamp",
182-
"//toolchain/install:install_paths",
183-
"@llvm-project//llvm:Support",
184-
],
185-
)

toolchain/driver/clang_runner.cpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ auto ClangRunner::Run(llvm::ArrayRef<llvm::StringRef> args) -> bool {
6868
// driver. Command lines can get quite long in build systems so this tries to
6969
// minimize the memory allocation overhead.
7070

71-
// Start with a dummy executable name. We'll manually set the install
72-
// directory below.
73-
std::array<llvm::StringRef, 1> exe_arg = {"clang-runner"};
71+
// Provide the wrapped `clang` path in order to support subprocessing. We also
72+
// set the install directory below.
73+
std::string clang_path = installation_->clang_path();
74+
std::array<llvm::StringRef, 1> exe_arg = {clang_path};
7475
auto args_range =
7576
llvm::concat<const llvm::StringRef>(exe_arg, maybe_v_arg, args);
7677
int total_size = 0;
@@ -95,6 +96,17 @@ auto ClangRunner::Run(llvm::ArrayRef<llvm::StringRef> args) -> bool {
9596
CARBON_VLOG(" '{0}'\n", cstr_arg);
9697
}
9798

99+
if (!args.empty() && args[0].starts_with("-cc1")) {
100+
CARBON_VLOG("Calling clang_main for cc1...");
101+
// cstr_args[0] will be the `clang_path` so we don't need the prepend arg.
102+
llvm::ToolContext tool_context = {
103+
.Path = cstr_args[0], .PrependArg = "clang", .NeedsPrependArg = false};
104+
int exit_code = clang_main(
105+
cstr_args.size(), const_cast<char**>(cstr_args.data()), tool_context);
106+
// TODO: Should this be forwarding the full exit code?
107+
return exit_code == 0;
108+
}
109+
98110
CARBON_VLOG("Preparing Clang driver...\n");
99111

100112
// Create the diagnostic options and parse arguments controlling them out of
@@ -113,7 +125,7 @@ auto ClangRunner::Run(llvm::ArrayRef<llvm::StringRef> args) -> bool {
113125
/*ShouldOwnClient=*/false);
114126
clang::ProcessWarningOptions(diagnostics, *diagnostic_options);
115127

116-
clang::driver::Driver driver("clang-runner", target_, diagnostics);
128+
clang::driver::Driver driver(clang_path, target_, diagnostics);
117129

118130
// Configure the install directory to find other tools and data files.
119131
//
@@ -128,18 +140,14 @@ auto ClangRunner::Run(llvm::ArrayRef<llvm::StringRef> args) -> bool {
128140
// still subprocess. See `InProcess` comment at:
129141
// https://github.com/llvm/llvm-project/blob/86ce8e4504c06ecc3cc42f002ad4eb05cac10925/clang/lib/Driver/Job.cpp#L411-L413
130142
//
131-
// TODO: It would be nice to find a way to set up the driver's understanding
132-
// of the executable name in a way that causes the multiple `cc1` invocations
133-
// to actually result in `carbon clang -- ...` invocations (even if as
134-
// subprocesses). This may dovetail with having symlinks that redirect to a
135-
// busybox of LLD as well, and having even the subprocesses consistently run
136-
// the Carbon install toolchain and not a system toolchain whenever possible.
137-
driver.CC1Main = [](llvm::SmallVectorImpl<const char*>& argv) -> int {
138-
// TODO: Try to use a better path for argv[0] (maybe in the LLVM install
139-
// paths). This works for now.
143+
// Note the subprocessing will effectively call `clang -cc1`, which turns into
144+
// `carbon-busybox clang -cc1`, which results in an equivalent `clang_main`
145+
// call.
146+
driver.CC1Main = [](llvm::SmallVectorImpl<const char*>& cc1_args) -> int {
147+
// cc1_args[0] will be the `clang_path` so we don't need the prepend arg.
140148
llvm::ToolContext tool_context = {
141-
.Path = argv[0], .PrependArg = "clang", .NeedsPrependArg = true};
142-
return clang_main(argv.size(), const_cast<char**>(argv.data()),
149+
.Path = cc1_args[0], .PrependArg = "clang", .NeedsPrependArg = false};
150+
return clang_main(cc1_args.size(), const_cast<char**>(cc1_args.data()),
143151
tool_context);
144152
};
145153

toolchain/driver/driver.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ auto Options::Build(CommandLine::CommandBuilder& b) -> void {
8888
}
8989

9090
auto Driver::RunCommand(llvm::ArrayRef<llvm::StringRef> args) -> DriverResult {
91+
if (driver_env_.installation->error()) {
92+
llvm::errs() << "error: " << *driver_env_.installation->error() << "\n";
93+
return {.success = false};
94+
}
95+
9196
Options options;
9297

9398
CommandLine::ParseResult result = CommandLine::Parse(

toolchain/driver/driver_main.cpp

Lines changed: 0 additions & 38 deletions
This file was deleted.

toolchain/install/BUILD

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,25 @@ cc_library(
7070
],
7171
)
7272

73+
# This target doesn't include prelude libraries. To get a target that has the
74+
# prelude available, use //toolchain.
75+
cc_binary(
76+
name = "carbon-busybox",
77+
srcs = ["busybox_main.cpp"],
78+
env = cc_env(),
79+
deps = [
80+
":install_paths",
81+
"//common:all_llvm_targets",
82+
"//common:bazel_working_dir",
83+
"//common:error",
84+
"//common:exe_path",
85+
"//common:init_llvm",
86+
"//common:version_stamp",
87+
"//toolchain/driver",
88+
"@llvm-project//llvm:Support",
89+
],
90+
)
91+
7392
lld_aliases = [
7493
"ld.lld",
7594
"ld64.lld",
@@ -88,15 +107,20 @@ lld_aliases = [
88107
# based on the FHS (Filesystem Hierarchy Standard).
89108
install_dirs = {
90109
"bin": [
91-
install_target(
110+
install_symlink(
92111
"carbon",
93-
"//toolchain/driver:carbon",
94-
executable = True,
112+
"../lib/carbon/carbon-busybox",
95113
is_driver = True,
96114
),
97115
],
98116
"lib/carbon": [
99117
install_target("carbon_install.txt", "carbon_install.txt"),
118+
install_target(
119+
"carbon-busybox",
120+
":carbon-busybox",
121+
executable = True,
122+
is_driver = True,
123+
),
100124
install_filegroup("core", "//core:prelude"),
101125
],
102126
"lib/carbon/llvm/bin": [
@@ -105,6 +129,11 @@ install_dirs = {
105129
"@llvm-project//lld:lld",
106130
executable = True,
107131
),
132+
install_symlink(
133+
"clang",
134+
"../../carbon-busybox",
135+
is_driver = True,
136+
),
108137
] + [install_symlink(name, "lld") for name in lld_aliases],
109138
}
110139

@@ -123,6 +152,9 @@ pkg_naming_variables(
123152
# We build both a compressed and uncompressed tar file with the same code here.
124153
# This lets us use the tar file in testing as it is fast to create, but ship the
125154
# compressed version as a release.
155+
#
156+
# For manual tests, the tar rules are `carbon_toolchain_tar_rule` and
157+
# `carbon_toolchain_tar_gz_rule`.
126158
pkg_tar_and_test(
127159
srcs = [":pkg_data"],
128160
name_base = "carbon_toolchain",

toolchain/install/busybox_main.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#include <unistd.h>
6+
7+
#include <cstdlib>
8+
#include <filesystem>
9+
10+
#include "common/bazel_working_dir.h"
11+
#include "common/error.h"
12+
#include "common/exe_path.h"
13+
#include "common/init_llvm.h"
14+
#include "llvm/ADT/SmallVector.h"
15+
#include "llvm/ADT/StringRef.h"
16+
#include "llvm/Support/LLVMDriver.h"
17+
#include "toolchain/driver/driver.h"
18+
#include "toolchain/install/install_paths.h"
19+
20+
namespace Carbon {
21+
22+
namespace {
23+
struct BusyboxInfo {
24+
// The path to `carbon-busybox`.
25+
std::filesystem::path bin_path;
26+
// The mode, such as `carbon` or `clang`.
27+
std::optional<std::string> mode;
28+
};
29+
} // namespace
30+
31+
// Returns the busybox information, given argv[0]. This primarily handles
32+
// resolving symlinks that point at the busybox.
33+
static auto GetBusyboxInfo(llvm::StringRef argv0) -> ErrorOr<BusyboxInfo> {
34+
BusyboxInfo info = BusyboxInfo{argv0.str(), std::nullopt};
35+
while (true) {
36+
std::string filename = info.bin_path.filename();
37+
if (filename == "carbon-busybox") {
38+
return info;
39+
}
40+
std::error_code ec;
41+
auto symlink_target = std::filesystem::read_symlink(info.bin_path, ec);
42+
if (ec) {
43+
return ErrorBuilder()
44+
<< "expected carbon-busybox symlink at `" << info.bin_path << "`";
45+
}
46+
info.mode = filename;
47+
info.bin_path = symlink_target;
48+
}
49+
}
50+
51+
// The actual `main` implementation. Can return an exit code or an `Error`
52+
// (which causes EXIT_FAILRUE).
53+
static auto Main(int argc, char** argv) -> ErrorOr<int> {
54+
Carbon::InitLLVM init_llvm(argc, argv);
55+
56+
// Start by resolving any symlinks.
57+
CARBON_ASSIGN_OR_RETURN(auto busybox_info, Carbon::GetBusyboxInfo(argv[0]));
58+
59+
auto fs = llvm::vfs::getRealFileSystem();
60+
61+
// Resolve paths before calling SetWorkingDirForBazel.
62+
std::string exe_path =
63+
Carbon::FindExecutablePath(busybox_info.bin_path.string());
64+
const auto install_paths = Carbon::InstallPaths::MakeExeRelative(exe_path);
65+
if (install_paths.error()) {
66+
return Error(*install_paths.error());
67+
}
68+
69+
Carbon::SetWorkingDirForBazel();
70+
71+
llvm::SmallVector<llvm::StringRef> args;
72+
args.reserve(argc + 1);
73+
if (busybox_info.mode && busybox_info.mode != "carbon") {
74+
args.append({*busybox_info.mode, "--"});
75+
}
76+
args.append(argv + 1, argv + argc);
77+
78+
Carbon::Driver driver(*fs, &install_paths, llvm::outs(), llvm::errs());
79+
bool success = driver.RunCommand(args).success;
80+
return success ? EXIT_SUCCESS : EXIT_FAILURE;
81+
}
82+
83+
} // namespace Carbon
84+
85+
auto main(int argc, char** argv) -> int {
86+
auto result = Carbon::Main(argc, argv);
87+
if (result.ok()) {
88+
return *result;
89+
} else {
90+
llvm::errs() << "error: " << result.error() << "\n";
91+
return EXIT_FAILURE;
92+
}
93+
}

toolchain/install/install_filegroups.bzl

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,19 @@ def install_filegroup(name, filegroup_target):
2222
"name": name,
2323
}
2424

25-
def install_symlink(name, symlink_to):
25+
def install_symlink(name, symlink_to, is_driver = False):
2626
"""Adds a symlink for install.
2727
2828
Used in the `install_dirs` dict.
2929
3030
Args:
3131
name: The filename to use.
3232
symlink_to: A relative path for the symlink.
33+
is_driver: False if it should be included in the `no_driver_name`
34+
filegroup.
3335
"""
3436
return {
35-
"is_driver": False,
37+
"is_driver": is_driver,
3638
"name": name,
3739
"symlink": symlink_to,
3840
}
@@ -117,10 +119,23 @@ def make_install_filegroups(name, no_driver_name, pkg_name, install_dirs, prefix
117119
)
118120
elif "symlink" in entry:
119121
symlink_to = "{0}/{1}/{2}".format(prefix, dir, entry["symlink"])
122+
123+
# For bazel, we need to resolve relative symlinks.
124+
if "../" in symlink_to:
125+
parts = symlink_to.split("/")
126+
result = []
127+
for part in parts:
128+
if part == "..":
129+
result = result[:-1]
130+
else:
131+
result.append(part)
132+
symlink_to = "/".join(result)
120133
symlink_file(
121134
name = prefixed_path,
122-
symlink_label = symlink_to,
135+
symlink_binary = symlink_to,
123136
)
137+
138+
# For the distributed package, we retain relative symlinks.
124139
pkg_mklink(
125140
name = pkg_path,
126141
link_name = path,

toolchain/install/install_paths.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ auto InstallPaths::MakeExeRelative(llvm::StringRef exe_path) -> InstallPaths {
4242
// FHS-like install prefix. We remove the filename and walk up to find the
4343
// expected install prefix.
4444
llvm::sys::path::remove_filename(paths.prefix_);
45-
llvm::sys::path::append(paths.prefix_, llvm::sys::path::Style::posix, "../");
45+
llvm::sys::path::append(paths.prefix_, llvm::sys::path::Style::posix,
46+
"../../");
4647

4748
if (auto error = llvm::sys::fs::make_absolute(paths.prefix_)) {
4849
paths.SetError(error.message());
@@ -162,4 +163,12 @@ auto InstallPaths::llvm_install_bin() const -> std::string {
162163
return path.str().str();
163164
}
164165

166+
auto InstallPaths::clang_path() const -> std::string {
167+
llvm::SmallString<256> path(prefix_);
168+
// TODO: Adjust this to work equally well on Windows.
169+
llvm::sys::path::append(path, llvm::sys::path::Style::posix,
170+
"lib/carbon/llvm/bin/clang");
171+
return path.str().str();
172+
}
173+
165174
} // namespace Carbon

0 commit comments

Comments
 (0)