| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- // 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/config_subcommand.h"
- #include <memory>
- #include <optional>
- #include <string>
- #include <utility>
- #include <variant>
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Lex/HeaderSearch.h"
- #include "common/check.h"
- #include "common/command_line.h"
- #include "common/filesystem.h"
- #include "common/version.h"
- #include "llvm/ADT/IntrusiveRefCntPtr.h"
- #include "llvm/ADT/STLExtras.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/Support/FormatVariadic.h"
- #include "llvm/Support/raw_ostream.h"
- #include "llvm/TargetParser/Triple.h"
- #include "toolchain/base/clang_invocation.h"
- #include "toolchain/diagnostics/consumer.h"
- #include "toolchain/diagnostics/diagnostic.h"
- #include "toolchain/diagnostics/emitter.h"
- #include "toolchain/driver/clang_runner.h"
- #include "toolchain/driver/driver_env.h"
- #include "toolchain/driver/driver_subcommand.h"
- namespace Carbon {
- auto ConfigOptions::Build(CommandLine::CommandBuilder& b) -> void {
- b.AddFlag(
- {
- .name = "json",
- .help = R"""(
- Render output as a JSON map for easy parsing.
- )""",
- },
- [&](auto& arg_b) {
- arg_b.Default(false);
- arg_b.Set(&json_output);
- });
- codegen_options.Build(b);
- }
- static constexpr CommandLine::CommandInfo SubcommandInfo = {
- .name = "config",
- .help = R"""(
- Print configuration info for the Carbon toolchain.
- This subcommand displays configuration information for the Carbon toolchain.
- This can be useful for build systems as well as debugging issues.
- )""",
- };
- ConfigSubcommand::ConfigSubcommand() : DriverSubcommand(SubcommandInfo) {}
- namespace {
- struct ConfigDataEntry {
- std::string key;
- std::variant<std::string, llvm::SmallVector<std::string>> value;
- };
- } // namespace
- // Creates a Clang invocation and queries it for config data to render.
- //
- // This includes the sysroot and the include directories searched during
- // compilation.
- //
- // If there are any errors setting up Clang, this will diagnose them using
- // `driver_env.consumer` and return `false`. If successful, returns `true`.
- static auto ComputeClangConfig(DriverEnv& driver_env,
- llvm::StringRef target_str,
- llvm::SmallVectorImpl<ConfigDataEntry>& data)
- -> bool {
- // Build a library invocation of Clang in order to query its header search
- // paths.
- std::shared_ptr clang_invocation =
- BuildClangInvocation(driver_env.consumer, driver_env.fs,
- *driver_env.installation, target_str, {});
- clang_invocation->getFrontendOpts().DisableFree = false;
- // Setup up a driver-style diagnostic engine for the compiler invocation and
- // instance below as we won't go past that while computing the include dirs.
- Diagnostics::ErrorTrackingConsumer error_tracker(driver_env.consumer);
- Diagnostics::NoLocEmitter emitter(&error_tracker);
- ClangDriverDiagnosticConsumer diagnostic_consumer(&emitter);
- llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
- clang::CompilerInstance::createDiagnostics(
- *driver_env.fs, clang_invocation->getDiagnosticOpts(),
- &diagnostic_consumer,
- /*ShouldOwnClient=*/false));
- auto clang_instance =
- std::make_unique<clang::CompilerInstance>(clang_invocation);
- clang_instance->setDiagnostics(diags);
- clang_instance->setVirtualFileSystem(driver_env.fs);
- clang_instance->createFileManager();
- clang_instance->createSourceManager();
- if (!clang_instance->createTarget()) {
- CARBON_DIAGNOSTIC(ConfigFailedToSetupTarget, Error,
- "unable to setup the requested target `{0}`",
- std::string);
- driver_env.emitter.Emit(ConfigFailedToSetupTarget, target_str.str());
- return false;
- }
- auto header_search = std::make_unique<clang::HeaderSearch>(
- clang_instance->getHeaderSearchOpts(), clang_instance->getSourceManager(),
- clang_instance->getDiagnostics(), clang_instance->getLangOpts(),
- &clang_instance->getTarget());
- clang::ApplyHeaderSearchOptions(
- *header_search, clang_instance->getHeaderSearchOpts(),
- clang_instance->getLangOpts(), clang_instance->getTarget().getTriple());
- // If we ended up diagnosing any errors, just return. They will have been
- // converted to Carbon diagnostics.
- if (error_tracker.seen_error()) {
- return false;
- }
- data.push_back({.key = "CLANG_SYSROOT",
- .value = clang_instance->getHeaderSearchOpts().Sysroot});
- llvm::SmallVector<std::string> search_paths;
- for (const auto& search_dir : header_search->search_dir_range()) {
- search_paths.push_back(search_dir.getName().str());
- }
- data.push_back(
- {.key = "CLANG_INCLUDE_DIRS", .value = std::move(search_paths)});
- return true;
- }
- static auto RenderDataAsJson(llvm::ArrayRef<ConfigDataEntry> data,
- llvm::raw_ostream& out) -> void {
- out << "{\n";
- llvm::ListSeparator data_sep(",\n");
- for (const auto& entry : data) {
- out << data_sep << " \"" << entry.key << "\": ";
- if (const auto* value = std::get_if<std::string>(&entry.value)) {
- out << "\"" << *value << "\"";
- } else if (const auto* value =
- std::get_if<llvm::SmallVector<std::string>>(&entry.value)) {
- out << "[\n";
- llvm::ListSeparator element_sep(",\n");
- for (const std::string& value_element : *value) {
- out << element_sep << " \"" << value_element << "\"";
- }
- out << "\n ]";
- } else {
- CARBON_FATAL("Invalid value in config data entry!");
- }
- }
- out << "\n}\n";
- }
- static auto RenderData(llvm::ArrayRef<ConfigDataEntry> data,
- llvm::raw_ostream& out) -> void {
- for (const auto& entry : data) {
- out << entry.key << ":";
- if (const auto* value = std::get_if<std::string>(&entry.value)) {
- out << " " << *value << "\n";
- } else if (const auto* value =
- std::get_if<llvm::SmallVector<std::string>>(&entry.value)) {
- out << "\n";
- for (const std::string& value_element : *value) {
- out << " " << value_element << "\n";
- }
- } else {
- CARBON_FATAL("Invalid value in config data entry!");
- }
- }
- }
- auto ConfigSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
- bool result = true;
- // Start with basic data available from the driver or global constants.
- llvm::SmallVector<ConfigDataEntry> data = {
- {.key = "CLANG_RESOURCE_DIR",
- .value = driver_env.installation->clang_resource_path()},
- {.key = "INSTALL_ROOT", .value = driver_env.installation->root()},
- {.key = "LLVM_BINDIR",
- .value = driver_env.installation->llvm_install_bin()},
- {.key = "VERSION", .value = Version::String.str()},
- };
- // Try to read the installation digest and include that.
- auto read_result = Filesystem::Cwd().ReadFileToString(
- driver_env.installation->digest_path());
- if (!read_result.ok()) {
- CARBON_DIAGNOSTIC(ConfigFailedToReadDigest, Error,
- "unable to read the installation's digest file: {0}",
- std::string);
- driver_env.emitter.Emit(ConfigFailedToReadDigest,
- read_result.error().ToString());
- // Remember that we encountered an error but continue to give a minimally
- // useful `config` output.
- result = false;
- } else {
- data.push_back({.key = "INSTALL_DIGEST",
- .value = llvm::StringRef(*read_result).rtrim().str()});
- }
- // Compute and print Clang's config entries if we can. This will have been
- // diagnosed while computing, so just track if we hit errors.
- result &=
- ComputeClangConfig(driver_env, options_.codegen_options.target, data);
- llvm::sort(data, [](const ConfigDataEntry& lhs, const ConfigDataEntry& rhs) {
- return lhs.key < rhs.key;
- });
- if (options_.json_output) {
- RenderDataAsJson(data, *driver_env.output_stream);
- } else {
- RenderData(data, *driver_env.output_stream);
- }
- return {.success = result};
- }
- } // namespace Carbon
|