Files
carbon-lang/toolchain/driver/link_subcommand.cpp
T
Chandler CarruthandGeoff Romer fd70196c67 Introduce a runtimes caching and management layer (#6002)
This layer allows runtimes to be built on-demand but cached in a
consistent and re-usable location on the system. It handles careful
filesystem operations to ensure consistency even in the face of multiple
versions and build configurations.

This addresses a number of TODOs from the initial runtimes building
on-demand, and sets the stage to scale up to more runtimes.

This doesn't switch on-demand runtimes to be on by default, I wanted to
wait and make that change as a separate step.

---------

Co-authored-by: Geoff Romer <gromer@google.com>
2025-09-25 12:30:50 +00:00

139 lines
4.9 KiB
C++

// 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/link_subcommand.h"
#include "llvm/TargetParser/Triple.h"
#include "toolchain/driver/clang_runner.h"
namespace Carbon {
auto LinkOptions::Build(CommandLine::CommandBuilder& b) -> void {
b.AddStringPositionalArg(
{
.name = "OBJECT_FILE",
.help = R"""(
The input object files.
)""",
},
[&](auto& arg_b) {
arg_b.Required(true);
arg_b.Append(&object_filenames);
});
b.AddStringOption(
{
.name = "output",
.value_name = "FILE",
.help = R"""(
The linked file name. The output is always a linked binary.
)""",
},
[&](auto& arg_b) {
arg_b.Required(true);
arg_b.Set(&output_filename);
});
codegen_options.Build(b);
}
static void AddOSFlags(llvm::StringRef target,
llvm::SmallVectorImpl<llvm::StringRef>& args) {
llvm::Triple triple(target);
switch (triple.getOS()) {
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
// On macOS we need to set the sysroot to a viable SDK. Currently, this
// hard codes the path to be the unversioned symlink. The prefix is also
// hard coded in Homebrew and so this seems likely to work reasonably
// well. Homebrew and I suspect the Xcode Clang both have this hard coded
// at build time, so this seems reasonably safe but we can revisit if/when
// needed.
args.push_back(
"--sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
// We also need to insist on a modern linker, otherwise the driver tries
// too old and deprecated flags. The specific number here comes from an
// inspection of the Clang driver source code to understand where features
// were enabled, and this appears to be the latest version to control
// driver behavior.
//
// TODO: We should replace this with use of `lld` eventually.
args.push_back("-mlinker-version=705");
break;
default:
// By default, just let the Clang driver handle everything.
break;
}
}
static constexpr CommandLine::CommandInfo SubcommandInfo = {
.name = "link",
.help = R"""(
Link Carbon executables.
This subcommand links Carbon executables by combining object files.
TODO: Support linking binary libraries, both archives and shared libraries.
TODO: Support linking against binary libraries.
)""",
};
LinkSubcommand::LinkSubcommand() : DriverSubcommand(SubcommandInfo) {}
auto LinkSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
// TODO: Currently we use the Clang driver to link. This works well on Unix
// OSes but we likely need to directly build logic to invoke `link.exe` on
// Windows where `cl.exe` doesn't typically cover that logic.
// Use a reasonably large small vector here to minimize allocations. We expect
// to link reasonably large numbers of object files.
llvm::SmallVector<llvm::StringRef, 128> clang_args;
// We link using a C++ mode of the driver.
clang_args.push_back("--driver-mode=g++");
// Pass the target down to Clang to pick up the correct defaults.
std::string target_arg =
llvm::formatv("--target={0}", options_.codegen_options.target).str();
clang_args.push_back(target_arg);
// Use LLD, which we provide in our install directory, for linking.
clang_args.push_back("-fuse-ld=lld");
// Disable linking the C++ standard library until can build and ship it as
// part of the Carbon toolchain. This clearly won't work once we get into
// interop, but for now it avoids spurious failures and distraction. The plan
// is to build and bundle libc++ at which point we can replace this with
// pointing at our bundled library.
// TODO: Replace this when ready.
clang_args.push_back("-nostdlib++");
// Add OS-specific flags based on the target.
AddOSFlags(options_.codegen_options.target, clang_args);
clang_args.push_back("-o");
clang_args.push_back(options_.output_filename);
clang_args.append(options_.object_filenames.begin(),
options_.object_filenames.end());
ClangRunner runner(driver_env.installation, &driver_env.runtimes_cache,
driver_env.fs, driver_env.vlog_stream);
ErrorOr<bool> run_result = runner.Run(clang_args);
if (!run_result.ok()) {
// This is not a Clang failure, but a failure to even run Clang, so we need
// to diagnose it here.
CARBON_DIAGNOSTIC(FailureRunningClangToLink, Error,
"failure running `clang` to perform linking: {0}",
std::string);
driver_env.emitter.Emit(FailureRunningClangToLink,
run_result.error().message());
return {.success = false};
}
// Successfully ran Clang to perform the link, return its result.
return {.success = *run_result};
}
} // namespace Carbon