clang_invocation.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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/base/clang_invocation.h"
  5. #include <filesystem>
  6. #include <string>
  7. #include "clang/Frontend/CompilerInstance.h"
  8. #include "clang/Frontend/Utils.h"
  9. #include "common/string_helpers.h"
  10. #include "llvm/ADT/ArrayRef.h"
  11. #include "llvm/ADT/SmallVector.h"
  12. #include "llvm/ADT/StringRef.h"
  13. #include "llvm/Support/FormatVariadic.h"
  14. namespace Carbon {
  15. // The fake file name to use for the synthesized includes file.
  16. static constexpr const char IncludesFileName[] = "<carbon Cpp imports>";
  17. namespace {
  18. // Used to convert diagnostics from the Clang driver to Carbon diagnostics.
  19. class ClangDriverDiagnosticConsumer : public clang::DiagnosticConsumer {
  20. public:
  21. // Creates an instance with the location that triggers calling Clang.
  22. // `context` must not be null.
  23. explicit ClangDriverDiagnosticConsumer(Diagnostics::NoLocEmitter* emitter)
  24. : emitter_(emitter) {}
  25. // Generates a Carbon warning for each Clang warning and a Carbon error for
  26. // each Clang error or fatal.
  27. auto HandleDiagnostic(clang::DiagnosticsEngine::Level diag_level,
  28. const clang::Diagnostic& info) -> void override {
  29. DiagnosticConsumer::HandleDiagnostic(diag_level, info);
  30. llvm::SmallString<256> message;
  31. info.FormatDiagnostic(message);
  32. switch (diag_level) {
  33. case clang::DiagnosticsEngine::Ignored:
  34. case clang::DiagnosticsEngine::Note:
  35. case clang::DiagnosticsEngine::Remark: {
  36. // TODO: Emit notes and remarks.
  37. break;
  38. }
  39. case clang::DiagnosticsEngine::Warning:
  40. case clang::DiagnosticsEngine::Error:
  41. case clang::DiagnosticsEngine::Fatal: {
  42. CARBON_DIAGNOSTIC(CppInteropDriverWarning, Warning, "{0}", std::string);
  43. CARBON_DIAGNOSTIC(CppInteropDriverError, Error, "{0}", std::string);
  44. emitter_->Emit(diag_level == clang::DiagnosticsEngine::Warning
  45. ? CppInteropDriverWarning
  46. : CppInteropDriverError,
  47. message.str().str());
  48. break;
  49. }
  50. }
  51. }
  52. private:
  53. // Diagnostic emitter. Note that driver diagnostics don't have meaningful
  54. // locations attached.
  55. Diagnostics::NoLocEmitter* emitter_;
  56. };
  57. } // namespace
  58. auto BuildClangInvocation(Diagnostics::Consumer& consumer,
  59. llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
  60. const InstallPaths& install_paths,
  61. llvm::StringRef target_str,
  62. llvm::ArrayRef<llvm::StringRef> extra_args)
  63. -> std::unique_ptr<clang::CompilerInvocation> {
  64. Diagnostics::ErrorTrackingConsumer error_tracker(consumer);
  65. Diagnostics::NoLocEmitter emitter(&error_tracker);
  66. ClangDriverDiagnosticConsumer diagnostics_consumer(&emitter);
  67. llvm::SmallVector<std::string> args;
  68. args.push_back("--start-no-unused-arguments");
  69. AppendDefaultClangArgs(install_paths, target_str, args);
  70. args.push_back("--end-no-unused-arguments");
  71. args.append({
  72. llvm::formatv("--target={0}", target_str).str(),
  73. // Add our include file name as the input file, and force it to be
  74. // interpreted as C++.
  75. "-x",
  76. "c++",
  77. IncludesFileName,
  78. });
  79. // The clang driver inconveniently wants an array of `const char*`, so convert
  80. // the arguments.
  81. llvm::OwningArrayRef<char> cstr_arg_storage;
  82. llvm::SmallVector<const char*> cstr_args = BuildCStrArgs(
  83. install_paths.clang_path().native(), args, extra_args, cstr_arg_storage);
  84. // Build a diagnostics engine. Note that we don't have any diagnostic options
  85. // yet; they're produced by running the driver.
  86. clang::DiagnosticOptions driver_diag_opts;
  87. llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> driver_diags(
  88. clang::CompilerInstance::createDiagnostics(*fs, driver_diag_opts,
  89. &diagnostics_consumer,
  90. /*ShouldOwnClient=*/false));
  91. // Ask the driver to process the arguments and build a corresponding clang
  92. // frontend invocation.
  93. auto invocation =
  94. clang::createInvocation(cstr_args, {.Diags = driver_diags, .VFS = fs});
  95. // If Clang produced an error, throw away its invocation.
  96. if (error_tracker.seen_error()) {
  97. return nullptr;
  98. }
  99. return invocation;
  100. }
  101. auto AppendDefaultClangArgs(const InstallPaths& /*install_paths*/,
  102. llvm::StringRef target_str,
  103. llvm::SmallVectorImpl<std::string>& args) -> void {
  104. args.append({
  105. // Enable PIE by default, but allow it to be overridden by Clang
  106. // arguments. Clang's default is configurable, but we'd like our
  107. // defaults to be more stable.
  108. // TODO: Decide if we want this.
  109. "-fPIE",
  110. // Override the default linker to use.
  111. "-fuse-ld=lld",
  112. });
  113. // Add target-specific flags.
  114. llvm::Triple triple(target_str);
  115. switch (triple.getOS()) {
  116. case llvm::Triple::Darwin:
  117. case llvm::Triple::MacOSX:
  118. // On macOS we need to set the sysroot to a viable SDK. Currently, this
  119. // hard codes the path to be the unversioned symlink. The prefix is also
  120. // hard coded in Homebrew and so this seems likely to work reasonably
  121. // well. Homebrew and I suspect the Xcode Clang both have this hard coded
  122. // at build time, so this seems reasonably safe but we can revisit if/when
  123. // needed.
  124. args.push_back(
  125. "--sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
  126. // We also need to insist on a modern linker, otherwise the driver tries
  127. // too old and deprecated flags. The specific number here comes from an
  128. // inspection of the Clang driver source code to understand where features
  129. // were enabled, and this appears to be the latest version to control
  130. // driver behavior.
  131. //
  132. // TODO: We should replace this with use of `lld` eventually.
  133. args.push_back("-mlinker-version=705");
  134. break;
  135. default:
  136. break;
  137. }
  138. // TODO: Add flags for the installed runtimes using `install_paths`.
  139. }
  140. } // namespace Carbon