Files
carbon-lang/toolchain/driver/build_subcommand.cpp
T
Lucile Rose Nihlen 1a26b57732 add first implementation of carbon build subcommand (#7239)
This is the first draft of the implementation of
[p6333](https://docs.carbon-lang.dev/proposals/p6333.html).

The `build` subcommand shared logic with the `compile` and `link`
subcommands,
so I've moved some of the functionality in `compile` and `link` to
shared
`CompileDriver` and `LinkDriver` classes, respectively. This also
required exposing the
`CompileOptions` and `LinkOptions` subcommand structs for re-use.

There's still some work to do on the proposal, most notably the package
include automatic path resolution and import, and the refactors to
`carbon compile`.
2026-06-09 20:24:01 +00:00

128 lines
4.3 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/build_subcommand.h"
#include <filesystem>
#include "common/filesystem.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallString.h"
#include "toolchain/driver/compile_driver.h"
#include "toolchain/driver/link_driver.h"
namespace Carbon {
auto BuildSubcommandOptions::Build(CommandLine::CommandBuilder& b) -> void {
compile_options.BuildForBuildSubcommand(b);
link_options.BuildForBuildSubcommand(b);
b.AddFlag(
{
.name = "use-temp-dir",
.help = R"""(
Use a temporary directory for intermediate compilation artifacts.
When enabled (the default), carbon will compile all input files and necessary
dependencies into a temporary directory, before linking them into the final
output binary. If false, carbon will store the compilation artifacts as hashes
of the compiled input name in the current working directory.
)""",
},
[&](auto& arg_b) {
arg_b.Default(true);
arg_b.Set(&use_temp_dir);
});
}
static constexpr CommandLine::CommandInfo SubcommandInfo = {
.name = "build",
.help = R"""(
Compile and then link Carbon and C++ source code into a single executable.
)""",
};
BuildSubcommand::BuildSubcommand() : DriverSubcommand(SubcommandInfo) {}
auto BuildSubcommand::BuildOptions(CommandLine::CommandBuilder& b) -> void {
options_.Build(b);
}
auto BuildSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
if (driver_env.fuzzing && !options_.compile_options.clang_args.empty()) {
// Parsing specific Clang arguments can reach deep into
// external libraries that aren't fuzz clean.
TestAndDiagnoseIfFuzzingExternalLibraries(driver_env, "build");
return {.success = false};
}
std::optional<Filesystem::RemovingDir> temp_dir = std::nullopt;
auto temp_dir_path = std::filesystem::path("");
if (options_.use_temp_dir) {
if (auto d = Filesystem::MakeTmpDir(); !d.ok()) {
CARBON_DIAGNOSTIC(BuildTempDirectoryCreationError, Error, "{0}",
std::string);
driver_env.emitter.Emit(BuildTempDirectoryCreationError,
PrintToString(d.error()));
return {.success = false};
} else {
temp_dir = std::move(*d);
temp_dir_path = temp_dir->path();
}
}
auto on_exit = llvm::scope_exit([&]() {
// Clean up the temporary directory created for compile results.
if (temp_dir) {
auto remove_result = std::move(*temp_dir).Remove();
if (!remove_result.ok()) {
CARBON_DIAGNOSTIC(BuildTempDirectoryDeletionError, Error, "{0}",
std::string);
driver_env.emitter.Emit(BuildTempDirectoryDeletionError,
PrintToString(remove_result.error()));
}
}
});
auto compile_driver = CompileDriver(&options_.compile_options);
if (!compile_driver.Initialize(
driver_env, [&](llvm::StringRef input_filename) -> std::string {
return (temp_dir_path /
llvm::formatv("{0:x16}.o", HashValue(input_filename)).str())
.string();
})) {
return {.success = false};
}
auto compile_result = compile_driver.Compile(driver_env);
if (!compile_result.success) {
return compile_result;
}
// Compute the needed LinkOptions for the LinkDriver from the output of the
// compilation process.
options_.link_options.codegen_options =
options_.compile_options.codegen_options;
llvm::SmallString<256> output_filename;
if (options_.link_options.output_filename.empty()) {
output_filename = llvm::sys::path::filename(
compile_driver.units()[compile_driver.first_input_index()]
->input_filename());
llvm::sys::path::replace_extension(output_filename, "");
options_.link_options.output_filename = output_filename;
}
auto input_builder = [&](const std::unique_ptr<CompilationUnit>& unit) {
return unit->output_filename();
};
append_range(options_.link_options.object_filenames,
llvm::map_range(compile_driver.units(), input_builder));
auto link_driver = LinkDriver(&options_.link_options);
return link_driver.Link(driver_env);
}
} // namespace Carbon