diff --git a/toolchain/driver/build_runtimes_subcommand.cpp b/toolchain/driver/build_runtimes_subcommand.cpp index a00831a2347c..88bef6e13594 100644 --- a/toolchain/driver/build_runtimes_subcommand.cpp +++ b/toolchain/driver/build_runtimes_subcommand.cpp @@ -22,6 +22,17 @@ generation options. }, [&](auto& arg_b) { arg_b.Set(&directory); }); + b.AddFlag( + { + .name = "force", + .help = R"""( +Force re-creating the provided output path from scratch + +This will **remove** the provided output path and re-create it from scratch. +)""", + }, + [&](CommandLine::FlagBuilder& arg_b) { arg_b.Set(&force); }); + codegen_options.Build(b); } @@ -74,22 +85,19 @@ auto BuildRuntimesSubcommand::RunInternal(DriverEnv& driver_env) .target = options_.codegen_options.target.str()}; bool is_cache = options_.directory.empty(); - std::filesystem::path explicit_output_path = options_.directory.str(); - if (!is_cache) { - auto access_result = Filesystem::Cwd().Access(explicit_output_path); - if (access_result.ok()) { - return Error("output directory already exists"); - } - if (!access_result.error().no_entity()) { - return std::move(access_result).error(); - } - } + std::filesystem::path output_path = options_.directory.str(); CARBON_ASSIGN_OR_RETURN( - auto runtimes, - is_cache ? driver_env.runtimes_cache.Lookup(features) - : Runtimes::Make(explicit_output_path, driver_env.vlog_stream)); - CARBON_ASSIGN_OR_RETURN(auto tmp_dir, Filesystem::MakeTmpDir()); + auto runtimes, is_cache + ? driver_env.runtimes_cache.Lookup(features) + : Runtimes::Make(output_path, driver_env.vlog_stream)); + + if (options_.force) { + // Remove existing runtimes to force a rebuild. + CARBON_RETURN_IF_ERROR(runtimes.Remove(Runtimes::ClangResourceDir)); + CARBON_RETURN_IF_ERROR(runtimes.Remove(Runtimes::LibUnwind)); + CARBON_RETURN_IF_ERROR(runtimes.Remove(Runtimes::Libcxx)); + } ClangResourceDirBuilder resource_dir_builder(&runner, driver_env.thread_pool, llvm::Triple(features.target), diff --git a/toolchain/driver/build_runtimes_subcommand.h b/toolchain/driver/build_runtimes_subcommand.h index 510de758e419..fc2de015d7c1 100644 --- a/toolchain/driver/build_runtimes_subcommand.h +++ b/toolchain/driver/build_runtimes_subcommand.h @@ -22,6 +22,7 @@ struct BuildRuntimesOptions { CodegenOptions codegen_options; llvm::StringRef directory; + bool force; }; // Implements the link subcommand of the driver. diff --git a/toolchain/driver/driver.cpp b/toolchain/driver/driver.cpp index c8a8861115b0..0b4b1fe1a72c 100644 --- a/toolchain/driver/driver.cpp +++ b/toolchain/driver/driver.cpp @@ -206,9 +206,8 @@ auto Driver::RunCommand(llvm::ArrayRef args) -> DriverResult { driver_env_.runtimes_cache = std::move(*cache_result); if (!options.prebuilt_runtimes_path.empty()) { - auto result = Runtimes::Make( - std::filesystem::absolute(options.prebuilt_runtimes_path.str()), - driver_env_.vlog_stream); + auto result = Runtimes::OpenExisting(options.prebuilt_runtimes_path.str(), + driver_env_.vlog_stream); if (!result.ok()) { // TODO: We should provide a better diagnostic than the raw error. CARBON_DIAGNOSTIC(DriverPrebuiltRuntimesInvalid, Error, "{0}", diff --git a/toolchain/driver/runtimes_cache.cpp b/toolchain/driver/runtimes_cache.cpp index 837dd5925b31..3509ea2fba76 100644 --- a/toolchain/driver/runtimes_cache.cpp +++ b/toolchain/driver/runtimes_cache.cpp @@ -28,11 +28,23 @@ namespace Carbon { -auto Runtimes::Make(std::filesystem::path path, llvm::raw_ostream* vlog_stream) - -> ErrorOr { +static auto MakeAbsolute(std::filesystem::path path) + -> ErrorOr { if (!path.is_absolute()) { - return Error("Runtimes require an absolute path"); + std::error_code ec; + path = std::filesystem::absolute(path, ec); + if (ec) { + return Error(llvm::formatv("Unable to compute an absolute path: {0}", + ec.message())); + } } + return std::move(path); +} + +auto Runtimes::OpenExisting(std::filesystem::path path, + llvm::raw_ostream* vlog_stream) + -> ErrorOr { + CARBON_ASSIGN_OR_RETURN(path, MakeAbsolute(std::move(path))); CARBON_ASSIGN_OR_RETURN( Filesystem::Dir dir, @@ -40,6 +52,15 @@ auto Runtimes::Make(std::filesystem::path path, llvm::raw_ostream* vlog_stream) return Runtimes(std::move(path), std::move(dir), {}, {}, vlog_stream); } +auto Runtimes::Make(std::filesystem::path path, llvm::raw_ostream* vlog_stream) + -> ErrorOr { + CARBON_ASSIGN_OR_RETURN(path, MakeAbsolute(std::move(path))); + + CARBON_ASSIGN_OR_RETURN(Filesystem::Dir dir, + Filesystem::Cwd().CreateDirectories(path)); + return Runtimes(std::move(path), std::move(dir), {}, {}, vlog_stream); +} + auto Runtimes::Destroy() -> void { // Release the lock on the runtimes and close the lock file. flock_ = {}; @@ -66,6 +87,11 @@ auto Runtimes::Build(Component component) return BuildImpl(component, BuildLockDeadline, BuildLockPollInterval); } +auto Runtimes::Remove(Component component) -> ErrorOr { + CARBON_RETURN_IF_ERROR(base_dir_.Rmtree(ComponentPath(component))); + return Success(); +} + auto Runtimes::BuildImpl(Component component, Filesystem::Duration deadline, Filesystem::Duration poll_interval) -> ErrorOr> { diff --git a/toolchain/driver/runtimes_cache.h b/toolchain/driver/runtimes_cache.h index a2f54db04433..46f79b945744 100644 --- a/toolchain/driver/runtimes_cache.h +++ b/toolchain/driver/runtimes_cache.h @@ -52,9 +52,22 @@ class Runtimes { NumComponents, }; - // Creates a `Runtimes` object for a specific existing directory. + // Creates a `Runtimes` object for a specific, existing directory. // - // Requires that the `path` is an absolute path that is an existing directory. + // If `path` is a relative path, it will be resolved to an absolute path + // relative to the current working directory. + // + // If there is not a directory at `path`, this will return an error. + static auto OpenExisting(std::filesystem::path path, + llvm::raw_ostream* vlog_stream = nullptr) + -> ErrorOr; + + // Creates a `Runtimes` object for a specific directory. + // + // Opens or creates `path`, including any intervening directories needed. + // + // If `path` is a relative path, it will be resolved to an absolute path + // relative to the current working directory. static auto Make(std::filesystem::path path, llvm::raw_ostream* vlog_stream = nullptr) -> ErrorOr; @@ -87,25 +100,29 @@ class Runtimes { // The base directory for the runtimes. auto base_dir() const -> Filesystem::DirRef { return base_dir_; } - // Gets the path to an _existing_ Clang resource directory. - // - // Clang's resource directory contains all of the compiler-builtin runtime - // libraries, headers, and data files. - // - // This will return the path to the Clang resource directory if it exists in - // the runtimes tree. Otherwise, it will return an error. + // Gets the path to an _existing_ built component subdirectory of the + // runtimes. If the component subdirectory doesn't exist, returns an error. auto Get(Component component) -> ErrorOr; - // Builds or returns a Clang resource directory. + // Builds or returns a component subdirectory of the runtimes. // - // If there is an existing, built Clang resource directory, this will return - // its path, the same as `GetExistingClangResourceDir` would. However, if - // there is not yet a Clang resource directory in this runtimes tree, returns - // a `Builder` object that can be used to build and commit a Clang resource - // directory to this runtimes tree. + // If there is an existing, built component subdirectory, this will return its + // path, the same as `Get` would. However, if there is not yet a built + // subdirectory in this runtimes tree, returns a `Builder` object that can be + // used to build and commit a component subdirectory to this runtimes tree. auto Build(Component component) -> ErrorOr>; + // Removes a component subdirectory of the runtimes. + // + // This can be used to force a subsequent `Build` invocation to actually build + // the relevant component. It is a separate operation though because there is + // no way to atomically and reliably replace a component with a fresh build -- + // there is always a chance that a racing build occurs instead. The separate + // API surfaces this, but is still useful in inherently non-racing cases like + // direct build requests for a given set of runtimes. + auto Remove(Component component) -> ErrorOr; + private: friend Builder; friend Cache;