diff --git a/bazel/check_deps/BUILD b/bazel/check_deps/BUILD index 3164f5a50ac4..9f3dd566f88f 100644 --- a/bazel/check_deps/BUILD +++ b/bazel/check_deps/BUILD @@ -18,7 +18,7 @@ filegroup( "//language_server", "//migrate_cpp:rewriter", "//migrate_cpp/cpp_refactoring", - "//toolchain/driver:carbon", + "//toolchain/install:carbon-busybox", "//utils/treesitter", ], tags = ["manual"], diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 242b6e126b5e..375a30a7cd2e 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -3,7 +3,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test") -load("//bazel/cc_toolchains:defs.bzl", "cc_env") load("//testing/fuzzing:rules.bzl", "cc_fuzz_test") package(default_visibility = ["//visibility:public"]) @@ -165,21 +164,3 @@ cc_fuzz_test( "@llvm-project//llvm:Support", ], ) - -# This target doesn't include prelude libraries. To get a target that has the -# prelude available, use //toolchain. -cc_binary( - name = "carbon", - srcs = ["driver_main.cpp"], - env = cc_env(), - deps = [ - ":driver", - "//common:all_llvm_targets", - "//common:bazel_working_dir", - "//common:exe_path", - "//common:init_llvm", - "//common:version_stamp", - "//toolchain/install:install_paths", - "@llvm-project//llvm:Support", - ], -) diff --git a/toolchain/driver/clang_runner.cpp b/toolchain/driver/clang_runner.cpp index e495c2b7b6c0..b58efb3ae04a 100644 --- a/toolchain/driver/clang_runner.cpp +++ b/toolchain/driver/clang_runner.cpp @@ -68,9 +68,10 @@ auto ClangRunner::Run(llvm::ArrayRef args) -> bool { // driver. Command lines can get quite long in build systems so this tries to // minimize the memory allocation overhead. - // Start with a dummy executable name. We'll manually set the install - // directory below. - std::array exe_arg = {"clang-runner"}; + // Provide the wrapped `clang` path in order to support subprocessing. We also + // set the install directory below. + std::string clang_path = installation_->clang_path(); + std::array exe_arg = {clang_path}; auto args_range = llvm::concat(exe_arg, maybe_v_arg, args); int total_size = 0; @@ -95,6 +96,17 @@ auto ClangRunner::Run(llvm::ArrayRef args) -> bool { CARBON_VLOG(" '{0}'\n", cstr_arg); } + if (!args.empty() && args[0].starts_with("-cc1")) { + CARBON_VLOG("Calling clang_main for cc1..."); + // cstr_args[0] will be the `clang_path` so we don't need the prepend arg. + llvm::ToolContext tool_context = { + .Path = cstr_args[0], .PrependArg = "clang", .NeedsPrependArg = false}; + int exit_code = clang_main( + cstr_args.size(), const_cast(cstr_args.data()), tool_context); + // TODO: Should this be forwarding the full exit code? + return exit_code == 0; + } + CARBON_VLOG("Preparing Clang driver...\n"); // Create the diagnostic options and parse arguments controlling them out of @@ -113,7 +125,7 @@ auto ClangRunner::Run(llvm::ArrayRef args) -> bool { /*ShouldOwnClient=*/false); clang::ProcessWarningOptions(diagnostics, *diagnostic_options); - clang::driver::Driver driver("clang-runner", target_, diagnostics); + clang::driver::Driver driver(clang_path, target_, diagnostics); // Configure the install directory to find other tools and data files. // @@ -128,18 +140,14 @@ auto ClangRunner::Run(llvm::ArrayRef args) -> bool { // still subprocess. See `InProcess` comment at: // https://github.com/llvm/llvm-project/blob/86ce8e4504c06ecc3cc42f002ad4eb05cac10925/clang/lib/Driver/Job.cpp#L411-L413 // - // TODO: It would be nice to find a way to set up the driver's understanding - // of the executable name in a way that causes the multiple `cc1` invocations - // to actually result in `carbon clang -- ...` invocations (even if as - // subprocesses). This may dovetail with having symlinks that redirect to a - // busybox of LLD as well, and having even the subprocesses consistently run - // the Carbon install toolchain and not a system toolchain whenever possible. - driver.CC1Main = [](llvm::SmallVectorImpl& argv) -> int { - // TODO: Try to use a better path for argv[0] (maybe in the LLVM install - // paths). This works for now. + // Note the subprocessing will effectively call `clang -cc1`, which turns into + // `carbon-busybox clang -cc1`, which results in an equivalent `clang_main` + // call. + driver.CC1Main = [](llvm::SmallVectorImpl& cc1_args) -> int { + // cc1_args[0] will be the `clang_path` so we don't need the prepend arg. llvm::ToolContext tool_context = { - .Path = argv[0], .PrependArg = "clang", .NeedsPrependArg = true}; - return clang_main(argv.size(), const_cast(argv.data()), + .Path = cc1_args[0], .PrependArg = "clang", .NeedsPrependArg = false}; + return clang_main(cc1_args.size(), const_cast(cc1_args.data()), tool_context); }; diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index ec2ef86349bb..b1066b5e0036 100644 --- a/toolchain/driver/driver.cpp +++ b/toolchain/driver/driver.cpp @@ -88,6 +88,11 @@ auto Options::Build(CommandLine::CommandBuilder& b) -> void { } auto Driver::RunCommand(llvm::ArrayRef args) -> DriverResult { + if (driver_env_.installation->error()) { + llvm::errs() << "error: " << *driver_env_.installation->error() << "\n"; + return {.success = false}; + } + Options options; CommandLine::ParseResult result = CommandLine::Parse( diff --git a/toolchain/driver/driver_main.cpp b/toolchain/driver/driver_main.cpp deleted file mode 100644 index af0a908967b8..000000000000 --- a/toolchain/driver/driver_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// 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 - -#include "common/bazel_working_dir.h" -#include "common/exe_path.h" -#include "common/init_llvm.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "toolchain/driver/driver.h" -#include "toolchain/install/install_paths.h" - -auto main(int argc, char** argv) -> int { - Carbon::InitLLVM init_llvm(argc, argv); - - if (argc < 1) { - return EXIT_FAILURE; - } - - // Resolve paths before calling SetWorkingDirForBazel. - std::string exe_path = Carbon::FindExecutablePath(argv[0]); - const auto install_paths = Carbon::InstallPaths::MakeExeRelative(exe_path); - if (install_paths.error()) { - llvm::errs() << "error: " << *install_paths.error(); - return EXIT_FAILURE; - } - - Carbon::SetWorkingDirForBazel(); - - llvm::SmallVector args(argv + 1, argv + argc); - auto fs = llvm::vfs::getRealFileSystem(); - - Carbon::Driver driver(*fs, &install_paths, llvm::outs(), llvm::errs()); - bool success = driver.RunCommand(args).success; - return success ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/toolchain/install/BUILD b/toolchain/install/BUILD index 26489b04cf26..f91ef404dc8e 100644 --- a/toolchain/install/BUILD +++ b/toolchain/install/BUILD @@ -70,6 +70,25 @@ cc_library( ], ) +# This target doesn't include prelude libraries. To get a target that has the +# prelude available, use //toolchain. +cc_binary( + name = "carbon-busybox", + srcs = ["busybox_main.cpp"], + env = cc_env(), + deps = [ + ":install_paths", + "//common:all_llvm_targets", + "//common:bazel_working_dir", + "//common:error", + "//common:exe_path", + "//common:init_llvm", + "//common:version_stamp", + "//toolchain/driver", + "@llvm-project//llvm:Support", + ], +) + lld_aliases = [ "ld.lld", "ld64.lld", @@ -88,15 +107,20 @@ lld_aliases = [ # based on the FHS (Filesystem Hierarchy Standard). install_dirs = { "bin": [ - install_target( + install_symlink( "carbon", - "//toolchain/driver:carbon", - executable = True, + "../lib/carbon/carbon-busybox", is_driver = True, ), ], "lib/carbon": [ install_target("carbon_install.txt", "carbon_install.txt"), + install_target( + "carbon-busybox", + ":carbon-busybox", + executable = True, + is_driver = True, + ), install_filegroup("core", "//core:prelude"), ], "lib/carbon/llvm/bin": [ @@ -105,6 +129,11 @@ install_dirs = { "@llvm-project//lld:lld", executable = True, ), + install_symlink( + "clang", + "../../carbon-busybox", + is_driver = True, + ), ] + [install_symlink(name, "lld") for name in lld_aliases], } @@ -123,6 +152,9 @@ pkg_naming_variables( # We build both a compressed and uncompressed tar file with the same code here. # This lets us use the tar file in testing as it is fast to create, but ship the # compressed version as a release. +# +# For manual tests, the tar rules are `carbon_toolchain_tar_rule` and +# `carbon_toolchain_tar_gz_rule`. pkg_tar_and_test( srcs = [":pkg_data"], name_base = "carbon_toolchain", diff --git a/toolchain/install/busybox_main.cpp b/toolchain/install/busybox_main.cpp new file mode 100644 index 000000000000..4a081e879b74 --- /dev/null +++ b/toolchain/install/busybox_main.cpp @@ -0,0 +1,93 @@ +// 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 + +#include +#include + +#include "common/bazel_working_dir.h" +#include "common/error.h" +#include "common/exe_path.h" +#include "common/init_llvm.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/LLVMDriver.h" +#include "toolchain/driver/driver.h" +#include "toolchain/install/install_paths.h" + +namespace Carbon { + +namespace { +struct BusyboxInfo { + // The path to `carbon-busybox`. + std::filesystem::path bin_path; + // The mode, such as `carbon` or `clang`. + std::optional mode; +}; +} // namespace + +// Returns the busybox information, given argv[0]. This primarily handles +// resolving symlinks that point at the busybox. +static auto GetBusyboxInfo(llvm::StringRef argv0) -> ErrorOr { + BusyboxInfo info = BusyboxInfo{argv0.str(), std::nullopt}; + while (true) { + std::string filename = info.bin_path.filename(); + if (filename == "carbon-busybox") { + return info; + } + std::error_code ec; + auto symlink_target = std::filesystem::read_symlink(info.bin_path, ec); + if (ec) { + return ErrorBuilder() + << "expected carbon-busybox symlink at `" << info.bin_path << "`"; + } + info.mode = filename; + info.bin_path = symlink_target; + } +} + +// The actual `main` implementation. Can return an exit code or an `Error` +// (which causes EXIT_FAILRUE). +static auto Main(int argc, char** argv) -> ErrorOr { + Carbon::InitLLVM init_llvm(argc, argv); + + // Start by resolving any symlinks. + CARBON_ASSIGN_OR_RETURN(auto busybox_info, Carbon::GetBusyboxInfo(argv[0])); + + auto fs = llvm::vfs::getRealFileSystem(); + + // Resolve paths before calling SetWorkingDirForBazel. + std::string exe_path = + Carbon::FindExecutablePath(busybox_info.bin_path.string()); + const auto install_paths = Carbon::InstallPaths::MakeExeRelative(exe_path); + if (install_paths.error()) { + return Error(*install_paths.error()); + } + + Carbon::SetWorkingDirForBazel(); + + llvm::SmallVector args; + args.reserve(argc + 1); + if (busybox_info.mode && busybox_info.mode != "carbon") { + args.append({*busybox_info.mode, "--"}); + } + args.append(argv + 1, argv + argc); + + Carbon::Driver driver(*fs, &install_paths, llvm::outs(), llvm::errs()); + bool success = driver.RunCommand(args).success; + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} + +} // namespace Carbon + +auto main(int argc, char** argv) -> int { + auto result = Carbon::Main(argc, argv); + if (result.ok()) { + return *result; + } else { + llvm::errs() << "error: " << result.error() << "\n"; + return EXIT_FAILURE; + } +} diff --git a/toolchain/install/install_filegroups.bzl b/toolchain/install/install_filegroups.bzl index f2bb04b752e0..eb938af81e40 100644 --- a/toolchain/install/install_filegroups.bzl +++ b/toolchain/install/install_filegroups.bzl @@ -22,7 +22,7 @@ def install_filegroup(name, filegroup_target): "name": name, } -def install_symlink(name, symlink_to): +def install_symlink(name, symlink_to, is_driver = False): """Adds a symlink for install. Used in the `install_dirs` dict. @@ -30,9 +30,11 @@ def install_symlink(name, symlink_to): Args: name: The filename to use. symlink_to: A relative path for the symlink. + is_driver: False if it should be included in the `no_driver_name` + filegroup. """ return { - "is_driver": False, + "is_driver": is_driver, "name": name, "symlink": symlink_to, } @@ -117,10 +119,23 @@ def make_install_filegroups(name, no_driver_name, pkg_name, install_dirs, prefix ) elif "symlink" in entry: symlink_to = "{0}/{1}/{2}".format(prefix, dir, entry["symlink"]) + + # For bazel, we need to resolve relative symlinks. + if "../" in symlink_to: + parts = symlink_to.split("/") + result = [] + for part in parts: + if part == "..": + result = result[:-1] + else: + result.append(part) + symlink_to = "/".join(result) symlink_file( name = prefixed_path, - symlink_label = symlink_to, + symlink_binary = symlink_to, ) + + # For the distributed package, we retain relative symlinks. pkg_mklink( name = pkg_path, link_name = path, diff --git a/toolchain/install/install_paths.cpp b/toolchain/install/install_paths.cpp index 5064d3b30d14..eff97b6d80dd 100644 --- a/toolchain/install/install_paths.cpp +++ b/toolchain/install/install_paths.cpp @@ -42,7 +42,8 @@ auto InstallPaths::MakeExeRelative(llvm::StringRef exe_path) -> InstallPaths { // FHS-like install prefix. We remove the filename and walk up to find the // expected install prefix. llvm::sys::path::remove_filename(paths.prefix_); - llvm::sys::path::append(paths.prefix_, llvm::sys::path::Style::posix, "../"); + llvm::sys::path::append(paths.prefix_, llvm::sys::path::Style::posix, + "../../"); if (auto error = llvm::sys::fs::make_absolute(paths.prefix_)) { paths.SetError(error.message()); @@ -162,4 +163,12 @@ auto InstallPaths::llvm_install_bin() const -> std::string { return path.str().str(); } +auto InstallPaths::clang_path() 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/clang"); + return path.str().str(); +} + } // namespace Carbon diff --git a/toolchain/install/install_paths.h b/toolchain/install/install_paths.h index fd3888cb84b3..745d451f06a2 100644 --- a/toolchain/install/install_paths.h +++ b/toolchain/install/install_paths.h @@ -84,6 +84,9 @@ class InstallPaths { // The directory containing LLVM install binaries. Computed on demand. auto llvm_install_bin() const -> std::string; + // The path to `clang`. + auto clang_path() const -> std::string; + private: friend class InstallPathsTestPeer; diff --git a/toolchain/install/install_paths_test.cpp b/toolchain/install/install_paths_test.cpp index 839ef9701f4f..9a633f2d76a4 100644 --- a/toolchain/install/install_paths_test.cpp +++ b/toolchain/install/install_paths_test.cpp @@ -89,9 +89,9 @@ class InstallPathsTest : public ::testing::Test { std::unique_ptr test_runfiles_; }; -TEST_F(InstallPathsTest, PrefixRootDriver) { +TEST_F(InstallPathsTest, PrefixRootBusybox) { std::string installed_driver_path = test_runfiles_->Rlocation( - "carbon/toolchain/install/prefix_root/bin/carbon"); + "carbon/toolchain/install/prefix_root/lib/carbon/carbon-busybox"); auto paths = InstallPaths::MakeExeRelative(installed_driver_path); ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error();