bazel_build_clang_runtimes.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. #include <unistd.h>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <filesystem>
  8. #include <memory>
  9. #include <optional>
  10. #include <string>
  11. #include <system_error>
  12. #include <utility>
  13. #include "common/bazel_working_dir.h"
  14. #include "common/check.h"
  15. #include "common/command_line.h"
  16. #include "common/error.h"
  17. #include "common/exe_path.h"
  18. #include "common/filesystem.h"
  19. #include "common/init_llvm.h"
  20. #include "common/raw_string_ostream.h"
  21. #include "common/version.h"
  22. #include "llvm/ADT/ArrayRef.h"
  23. #include "llvm/ADT/STLExtras.h"
  24. #include "llvm/ADT/SmallVector.h"
  25. #include "llvm/ADT/StringRef.h"
  26. #include "llvm/Support/FormatVariadic.h"
  27. #include "llvm/Support/ThreadPool.h"
  28. #include "llvm/Support/Threading.h"
  29. #include "llvm/Support/VirtualFileSystem.h"
  30. #include "llvm/Support/raw_ostream.h"
  31. #include "llvm/TargetParser/Triple.h"
  32. #include "toolchain/base/install_paths.h"
  33. #include "toolchain/driver/clang_runner.h"
  34. #include "toolchain/driver/clang_runtimes.h"
  35. #include "toolchain/driver/codegen_options.h"
  36. #include "toolchain/driver/runtimes_cache.h"
  37. #include "tools/cpp/runfiles/runfiles.h"
  38. namespace Carbon {
  39. namespace {
  40. struct Options {
  41. static const CommandLine::CommandInfo Info;
  42. auto Build(CommandLine::CommandBuilder& b) -> void;
  43. bool verbose = false;
  44. bool force = false;
  45. bool threads = true;
  46. llvm::StringRef directory;
  47. CodegenOptions codegen_options;
  48. };
  49. } // namespace
  50. // Note that this is not constexpr so that it can include information generated
  51. // in separate translation units and potentially overridden at link time in the
  52. // version string.
  53. const CommandLine::CommandInfo Options::Info = {
  54. .name = "bazel_build_clang_runtimes",
  55. .version = Version::ToolchainInfo,
  56. .help = R"""(
  57. A dedicated tool for use with Bazel to build Carbon's _Clang_ runtimes.
  58. This works similarly to the Carbon `build-runtimes` subcommand with some key
  59. differences:
  60. 1) It only builds the Clang runtimes, not any Carbon-specific runtimes. This is
  61. important due to the next point...
  62. 2) It is a stand-alone command with minimal dependencies on Carbon to allow
  63. Bazel to cache the Clang-based runtimes across most changes to the Carbon
  64. toolchain when the LLVM version stays the same.
  65. 3) It removes any symlinks in the built runtimes tree into other parts of the
  66. installation that are not generated by this command. This allows a Bazel rule
  67. to re-create those in using Bazel-specific logic that connects those parts of
  68. the runtimes tree to their respective inputs.
  69. )""",
  70. };
  71. auto Options::Build(CommandLine::CommandBuilder& b) -> void {
  72. b.AddFlag(
  73. {
  74. .name = "verbose",
  75. .short_name = "v",
  76. .help = "Enable verbose logging to the stderr stream.",
  77. },
  78. [&](CommandLine::FlagBuilder& arg_b) { arg_b.Set(&verbose); });
  79. b.AddFlag(
  80. {
  81. .name = "force",
  82. .help = R"""(
  83. Force re-creating the provided output path from scratch
  84. This will **remove** the provided output path and re-create it from scratch.
  85. )""",
  86. },
  87. [&](CommandLine::FlagBuilder& arg_b) { arg_b.Set(&force); });
  88. b.AddFlag(
  89. {
  90. .name = "threads",
  91. .help = R"""(
  92. Controls whether threads are used to build runtimes.
  93. When enabled (the default), Carbon will try to build runtime libraries using
  94. threads to parallelize the operation. How many threads is controlled
  95. automatically by the system.
  96. Disabling threads ensures a single threaded build of the runtimes which can help
  97. when there are errors or other output.
  98. )""",
  99. },
  100. [&](auto& arg_b) {
  101. arg_b.Default(true);
  102. arg_b.Set(&threads);
  103. });
  104. codegen_options.Build(b);
  105. b.AddStringPositionalArg(
  106. {
  107. .name = "output-directory",
  108. .help = R"""(
  109. The directory to populate with runtime libraries suitable for the selected code
  110. generation options.
  111. )""",
  112. },
  113. [&](auto& arg_b) {
  114. arg_b.Required(true);
  115. arg_b.Set(&directory);
  116. });
  117. b.Do([] {});
  118. }
  119. static auto MakeInstallPaths(const std::filesystem::path& exe_path)
  120. -> InstallPaths {
  121. CARBON_CHECK(*Filesystem::Cwd().Access(exe_path,
  122. Filesystem::AccessCheckFlags::Execute),
  123. "Invoked with a non-executable `argv[0]`: {0}", exe_path);
  124. return InstallPaths::MakeForBazelRunfiles(exe_path.native());
  125. }
  126. static auto GetClangPath(const std::filesystem::path& exe_path)
  127. -> std::filesystem::path {
  128. std::string runtimes_error;
  129. using bazel::tools::cpp::runfiles::Runfiles;
  130. std::unique_ptr<Runfiles> runfiles(
  131. Runfiles::Create(exe_path.native(), &runtimes_error));
  132. CARBON_CHECK(runfiles != nullptr, "Failed to find runtimes tree: {0}",
  133. runtimes_error);
  134. std::filesystem::path clang_path =
  135. runfiles->Rlocation("llvm-project/clang/clang");
  136. CARBON_CHECK(!clang_path.empty());
  137. CARBON_CHECK(*Filesystem::Cwd().Access(clang_path));
  138. return clang_path;
  139. }
  140. static auto ParseOptions(int argc, char** argv) -> ErrorOr<Options> {
  141. Options options;
  142. llvm::SmallVector<llvm::StringRef> args(
  143. llvm::ArrayRef<char*>(argv, argc).drop_front());
  144. CARBON_ASSIGN_OR_RETURN(
  145. auto result, CommandLine::Parse(args, llvm::outs(), Options::Info,
  146. [&](CommandLine::CommandBuilder& b) {
  147. options.Build(b);
  148. }));
  149. if (result == CommandLine::ParseResult::MetaSuccess) {
  150. // Exit immediately with success if this was just a meta invocation.
  151. #if !defined(__APPLE__)
  152. std::quick_exit(0);
  153. #else
  154. // No `std::quick_exit` on macOS, despite the standard including it.
  155. _Exit(0);
  156. #endif
  157. }
  158. return options;
  159. }
  160. // The actual `main` implementation. Can return an exit code or an `Error`
  161. // (which causes EXIT_FAILURE).
  162. //
  163. // Note that this is primarily an internal utility for use with a specific set
  164. // of Bazel rules, and so many errors are directly `CARBON_CHECK`-ed instead of
  165. // propagated. Only basic command line errors are propagated using the `Error`
  166. // side of the return. Other errors in the execution environment are `CHECK`-ed
  167. // to provide useful backtraces when debugging.
  168. static auto Main(int argc, char** argv) -> ErrorOr<int> {
  169. InitLLVM init_llvm(argc, argv);
  170. if (argc < 1) {
  171. return Error("Invoked without command line arguments");
  172. }
  173. std::filesystem::path exe_path = argv[0];
  174. exe_path = SetWorkingDirForBazelRun(exe_path);
  175. const auto install_paths = MakeInstallPaths(exe_path);
  176. CARBON_ASSIGN_OR_RETURN(Options options, ParseOptions(argc, argv));
  177. std::filesystem::path clang_path = GetClangPath(exe_path);
  178. auto fs = llvm::vfs::getRealFileSystem();
  179. llvm::raw_ostream* vlog_stream = options.verbose ? &llvm::errs() : nullptr;
  180. ClangRunner runner(&install_paths, fs, vlog_stream, std::move(clang_path));
  181. Runtimes::Cache::Features features = {
  182. .target = options.codegen_options.target.str()};
  183. llvm::SingleThreadExecutor single_thread({.ThreadsRequested = 1});
  184. std::optional<llvm::DefaultThreadPool> threads;
  185. llvm::ThreadPoolInterface* thread_pool = &single_thread;
  186. if (options.threads) {
  187. threads.emplace(llvm::optimal_concurrency());
  188. thread_pool = &*threads;
  189. }
  190. auto runtimes = *Runtimes::Make(options.directory.str(), vlog_stream);
  191. if (options.force) {
  192. // Remove existing runtimes to force a rebuild.
  193. runtimes.Remove(Runtimes::ClangResourceDir).Check();
  194. runtimes.Remove(Runtimes::LibUnwind).Check();
  195. runtimes.Remove(Runtimes::Libcxx).Check();
  196. }
  197. ClangResourceDirBuilder resource_dir_builder(
  198. &runner, thread_pool, llvm::Triple(features.target), &runtimes);
  199. ClangArchiveRuntimesBuilder<Runtimes::LibUnwind> lib_unwind_builder(
  200. &runner, thread_pool, llvm::Triple(features.target), &runtimes);
  201. ClangArchiveRuntimesBuilder<Runtimes::Libcxx> libcxx_builder(
  202. &runner, thread_pool, llvm::Triple(features.target), &runtimes);
  203. std::filesystem::path resource_dir_path =
  204. *std::move(resource_dir_builder).Wait();
  205. std::move(lib_unwind_builder).Wait().Check();
  206. std::move(libcxx_builder).Wait().Check();
  207. // Now remove the `include` symlink from the resource_dir. We'll re-create
  208. // this tree in the Bazel rule, as the symlink currently is an absolute
  209. // (non-hermetic) path. We want Bazel to manage this directory with links to
  210. // the actual input files.
  211. Filesystem::Dir resource_dir = *Filesystem::Cwd().OpenDir(resource_dir_path);
  212. resource_dir.Unlink("include").Check();
  213. return EXIT_SUCCESS;
  214. }
  215. } // namespace Carbon
  216. auto main(int argc, char** argv) -> int {
  217. auto result = Carbon::Main(argc, argv);
  218. if (result.ok()) {
  219. return *result;
  220. } else {
  221. llvm::errs() << "error: " << result.error() << "\n";
  222. return EXIT_FAILURE;
  223. }
  224. }