config_subcommand.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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 "toolchain/driver/config_subcommand.h"
  5. #include <memory>
  6. #include <optional>
  7. #include <string>
  8. #include <utility>
  9. #include <variant>
  10. #include "clang/Frontend/CompilerInstance.h"
  11. #include "clang/Lex/HeaderSearch.h"
  12. #include "common/check.h"
  13. #include "common/command_line.h"
  14. #include "common/filesystem.h"
  15. #include "common/version.h"
  16. #include "llvm/ADT/IntrusiveRefCntPtr.h"
  17. #include "llvm/ADT/STLExtras.h"
  18. #include "llvm/ADT/SmallVector.h"
  19. #include "llvm/ADT/StringRef.h"
  20. #include "llvm/Support/FormatVariadic.h"
  21. #include "llvm/Support/raw_ostream.h"
  22. #include "llvm/TargetParser/Triple.h"
  23. #include "toolchain/base/clang_invocation.h"
  24. #include "toolchain/diagnostics/consumer.h"
  25. #include "toolchain/diagnostics/diagnostic.h"
  26. #include "toolchain/diagnostics/emitter.h"
  27. #include "toolchain/driver/clang_runner.h"
  28. #include "toolchain/driver/driver_env.h"
  29. #include "toolchain/driver/driver_subcommand.h"
  30. namespace Carbon {
  31. auto ConfigOptions::Build(CommandLine::CommandBuilder& b) -> void {
  32. b.AddFlag(
  33. {
  34. .name = "json",
  35. .help = R"""(
  36. Render output as a JSON map for easy parsing.
  37. )""",
  38. },
  39. [&](auto& arg_b) {
  40. arg_b.Default(false);
  41. arg_b.Set(&json_output);
  42. });
  43. codegen_options.Build(b);
  44. }
  45. static constexpr CommandLine::CommandInfo SubcommandInfo = {
  46. .name = "config",
  47. .help = R"""(
  48. Print configuration info for the Carbon toolchain.
  49. This subcommand displays configuration information for the Carbon toolchain.
  50. This can be useful for build systems as well as debugging issues.
  51. )""",
  52. };
  53. ConfigSubcommand::ConfigSubcommand() : DriverSubcommand(SubcommandInfo) {}
  54. namespace {
  55. struct ConfigDataEntry {
  56. std::string key;
  57. std::variant<std::string, llvm::SmallVector<std::string>> value;
  58. };
  59. } // namespace
  60. // Creates a Clang invocation and queries it for config data to render.
  61. //
  62. // This includes the sysroot and the include directories searched during
  63. // compilation.
  64. //
  65. // If there are any errors setting up Clang, this will diagnose them using
  66. // `driver_env.consumer` and return `false`. If successful, returns `true`.
  67. static auto ComputeClangConfig(DriverEnv& driver_env,
  68. llvm::StringRef target_str,
  69. llvm::SmallVectorImpl<ConfigDataEntry>& data)
  70. -> bool {
  71. // Build a library invocation of Clang in order to query its header search
  72. // paths.
  73. std::shared_ptr clang_invocation =
  74. BuildClangInvocation(driver_env.consumer, driver_env.fs,
  75. *driver_env.installation, target_str, {});
  76. clang_invocation->getFrontendOpts().DisableFree = false;
  77. // Setup up a driver-style diagnostic engine for the compiler invocation and
  78. // instance below as we won't go past that while computing the include dirs.
  79. Diagnostics::ErrorTrackingConsumer error_tracker(driver_env.consumer);
  80. Diagnostics::NoLocEmitter emitter(&error_tracker);
  81. ClangDriverDiagnosticConsumer diagnostic_consumer(&emitter);
  82. llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
  83. clang::CompilerInstance::createDiagnostics(
  84. *driver_env.fs, clang_invocation->getDiagnosticOpts(),
  85. &diagnostic_consumer,
  86. /*ShouldOwnClient=*/false));
  87. auto clang_instance =
  88. std::make_unique<clang::CompilerInstance>(clang_invocation);
  89. clang_instance->setDiagnostics(diags);
  90. clang_instance->setVirtualFileSystem(driver_env.fs);
  91. clang_instance->createFileManager();
  92. clang_instance->createSourceManager();
  93. if (!clang_instance->createTarget()) {
  94. CARBON_DIAGNOSTIC(ConfigFailedToSetupTarget, Error,
  95. "unable to setup the requested target `{0}`",
  96. std::string);
  97. driver_env.emitter.Emit(ConfigFailedToSetupTarget, target_str.str());
  98. return false;
  99. }
  100. auto header_search = std::make_unique<clang::HeaderSearch>(
  101. clang_instance->getHeaderSearchOpts(), clang_instance->getSourceManager(),
  102. clang_instance->getDiagnostics(), clang_instance->getLangOpts(),
  103. &clang_instance->getTarget());
  104. clang::ApplyHeaderSearchOptions(
  105. *header_search, clang_instance->getHeaderSearchOpts(),
  106. clang_instance->getLangOpts(), clang_instance->getTarget().getTriple());
  107. // If we ended up diagnosing any errors, just return. They will have been
  108. // converted to Carbon diagnostics.
  109. if (error_tracker.seen_error()) {
  110. return false;
  111. }
  112. data.push_back({.key = "CLANG_SYSROOT",
  113. .value = clang_instance->getHeaderSearchOpts().Sysroot});
  114. llvm::SmallVector<std::string> search_paths;
  115. for (const auto& search_dir : header_search->search_dir_range()) {
  116. search_paths.push_back(search_dir.getName().str());
  117. }
  118. data.push_back(
  119. {.key = "CLANG_INCLUDE_DIRS", .value = std::move(search_paths)});
  120. return true;
  121. }
  122. static auto RenderDataAsJson(llvm::ArrayRef<ConfigDataEntry> data,
  123. llvm::raw_ostream& out) -> void {
  124. out << "{\n";
  125. llvm::ListSeparator data_sep(",\n");
  126. for (const auto& entry : data) {
  127. out << data_sep << " \"" << entry.key << "\": ";
  128. if (const auto* value = std::get_if<std::string>(&entry.value)) {
  129. out << "\"" << *value << "\"";
  130. } else if (const auto* value =
  131. std::get_if<llvm::SmallVector<std::string>>(&entry.value)) {
  132. out << "[\n";
  133. llvm::ListSeparator element_sep(",\n");
  134. for (const std::string& value_element : *value) {
  135. out << element_sep << " \"" << value_element << "\"";
  136. }
  137. out << "\n ]";
  138. } else {
  139. CARBON_FATAL("Invalid value in config data entry!");
  140. }
  141. }
  142. out << "\n}\n";
  143. }
  144. static auto RenderData(llvm::ArrayRef<ConfigDataEntry> data,
  145. llvm::raw_ostream& out) -> void {
  146. for (const auto& entry : data) {
  147. out << entry.key << ":";
  148. if (const auto* value = std::get_if<std::string>(&entry.value)) {
  149. out << " " << *value << "\n";
  150. } else if (const auto* value =
  151. std::get_if<llvm::SmallVector<std::string>>(&entry.value)) {
  152. out << "\n";
  153. for (const std::string& value_element : *value) {
  154. out << " " << value_element << "\n";
  155. }
  156. } else {
  157. CARBON_FATAL("Invalid value in config data entry!");
  158. }
  159. }
  160. }
  161. auto ConfigSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
  162. bool result = true;
  163. // Start with basic data available from the driver or global constants.
  164. llvm::SmallVector<ConfigDataEntry> data = {
  165. {.key = "CLANG_RESOURCE_DIR",
  166. .value = driver_env.installation->clang_resource_path()},
  167. {.key = "INSTALL_ROOT", .value = driver_env.installation->root()},
  168. {.key = "LLVM_BINDIR",
  169. .value = driver_env.installation->llvm_install_bin()},
  170. {.key = "VERSION", .value = Version::String.str()},
  171. };
  172. // Try to read the installation digest and include that.
  173. auto read_result = Filesystem::Cwd().ReadFileToString(
  174. driver_env.installation->digest_path());
  175. if (!read_result.ok()) {
  176. CARBON_DIAGNOSTIC(ConfigFailedToReadDigest, Error,
  177. "unable to read the installation's digest file: {0}",
  178. std::string);
  179. driver_env.emitter.Emit(ConfigFailedToReadDigest,
  180. read_result.error().ToString());
  181. // Remember that we encountered an error but continue to give a minimally
  182. // useful `config` output.
  183. result = false;
  184. } else {
  185. data.push_back({.key = "INSTALL_DIGEST",
  186. .value = llvm::StringRef(*read_result).rtrim().str()});
  187. }
  188. // Compute and print Clang's config entries if we can. This will have been
  189. // diagnosed while computing, so just track if we hit errors.
  190. result &=
  191. ComputeClangConfig(driver_env, options_.codegen_options.target, data);
  192. llvm::sort(data, [](const ConfigDataEntry& lhs, const ConfigDataEntry& rhs) {
  193. return lhs.key < rhs.key;
  194. });
  195. if (options_.json_output) {
  196. RenderDataAsJson(data, *driver_env.output_stream);
  197. } else {
  198. RenderData(data, *driver_env.output_stream);
  199. }
  200. return {.success = result};
  201. }
  202. } // namespace Carbon