clang_invocation.cpp 6.6 KB

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