| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- // 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/base/clang_invocation.h"
- #include <filesystem>
- #include <string>
- #include "clang/Driver/CreateInvocationFromArgs.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Frontend/Utils.h"
- #include "common/string_helpers.h"
- #include "llvm/ADT/ArrayRef.h"
- #include "llvm/ADT/SmallVector.h"
- #include "llvm/ADT/StringRef.h"
- #include "llvm/Support/FormatVariadic.h"
- namespace Carbon {
- // The fake file name to use for the synthesized includes file.
- static constexpr const char IncludesFileName[] = "<carbon Cpp imports>";
- auto ClangDriverDiagnosticConsumer::HandleDiagnostic(
- clang::DiagnosticsEngine::Level diag_level, const clang::Diagnostic& info)
- -> void {
- DiagnosticConsumer::HandleDiagnostic(diag_level, info);
- llvm::SmallString<256> message;
- info.FormatDiagnostic(message);
- switch (diag_level) {
- case clang::DiagnosticsEngine::Ignored:
- case clang::DiagnosticsEngine::Note:
- case clang::DiagnosticsEngine::Remark: {
- // TODO: Emit notes and remarks.
- break;
- }
- case clang::DiagnosticsEngine::Warning:
- case clang::DiagnosticsEngine::Error:
- case clang::DiagnosticsEngine::Fatal: {
- CARBON_DIAGNOSTIC(CppInteropDriverWarning, Warning, "{0}", std::string);
- CARBON_DIAGNOSTIC(CppInteropDriverError, Error, "{0}", std::string);
- emitter_->Emit(diag_level == clang::DiagnosticsEngine::Warning
- ? CppInteropDriverWarning
- : CppInteropDriverError,
- message.str().str());
- break;
- }
- }
- }
- auto BuildClangInvocation(Diagnostics::Consumer& consumer,
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
- const InstallPaths& install_paths,
- llvm::StringRef target_str,
- llvm::ArrayRef<llvm::StringRef> extra_args)
- -> std::unique_ptr<clang::CompilerInvocation> {
- Diagnostics::ErrorTrackingConsumer error_tracker(consumer);
- Diagnostics::NoLocEmitter emitter(&error_tracker);
- ClangDriverDiagnosticConsumer diagnostics_consumer(&emitter);
- llvm::SmallVector<std::string> args;
- args.push_back("--start-no-unused-arguments");
- AppendDefaultClangArgs(install_paths, target_str, args);
- args.push_back("--end-no-unused-arguments");
- args.append({
- llvm::formatv("--target={0}", target_str).str(),
- // Add our include file name as the input file, and force it to be
- // interpreted as C++.
- "-x",
- "c++",
- IncludesFileName,
- });
- // The clang driver inconveniently wants an array of `const char*`, so convert
- // the arguments.
- llvm::BumpPtrAllocator alloc;
- llvm::SmallVector<const char*> cstr_args = BuildCStrArgs(
- install_paths.clang_path().native(), args, extra_args, alloc);
- // Build a diagnostics engine. Note that we don't have any diagnostic options
- // yet; they're produced by running the driver.
- clang::DiagnosticOptions driver_diag_opts;
- llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> driver_diags(
- clang::CompilerInstance::createDiagnostics(*fs, driver_diag_opts,
- &diagnostics_consumer,
- /*ShouldOwnClient=*/false));
- // Ask the driver to process the arguments and build a corresponding clang
- // frontend invocation.
- auto invocation =
- clang::createInvocation(cstr_args, {.Diags = driver_diags, .VFS = fs});
- // If Clang produced an error, throw away its invocation.
- if (error_tracker.seen_error()) {
- return nullptr;
- }
- if (invocation) {
- // Do not emit Clang's name and version as the creator of the output file.
- invocation->getCodeGenOpts().EmitVersionIdentMetadata = false;
- invocation->getCodeGenOpts().DiscardValueNames = false;
- }
- return invocation;
- }
- auto AppendDefaultClangArgs(const InstallPaths& install_paths,
- llvm::StringRef target_str,
- llvm::SmallVectorImpl<std::string>& args) -> void {
- args.append({
- // Enable PIE by default, but allow it to be overridden by Clang
- // arguments. Clang's default is configurable, but we'd like our
- // defaults to be more stable.
- // TODO: Decide if we want this.
- "-fPIE",
- // Enable function and data sections by default, and don't waste object
- // file size on unique section names. Allow these to be overridden by
- // Clang arguments.
- "-ffunction-sections",
- "-fdata-sections",
- "-fno-unique-section-names",
- // Override runtime library defaults.
- //
- // TODO: We should consider if there is a reasonable way to build Clang
- // with its configuration macros set to establish these defaults rather
- // than doing it with runtime flags.
- "-rtlib=compiler-rt",
- "-unwindlib=libunwind",
- "-stdlib=libc++",
- // Override the default linker to use.
- "-fuse-ld=lld",
- });
- // Add target-specific flags.
- llvm::Triple triple(target_str);
- switch (triple.getOS()) {
- case llvm::Triple::Darwin:
- case llvm::Triple::MacOSX:
- // On macOS we need to set the sysroot to a viable SDK. Currently, this
- // hard codes the path to be the unversioned symlink. The prefix is also
- // hard coded in Homebrew and so this seems likely to work reasonably
- // well. Homebrew and I suspect the Xcode Clang both have this hard coded
- // at build time, so this seems reasonably safe but we can revisit if/when
- // needed.
- args.push_back(
- "--sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
- // We also need to insist on a modern linker, otherwise the driver tries
- // too old and deprecated flags. The specific number here comes from an
- // inspection of the Clang driver source code to understand where features
- // were enabled, and this appears to be the latest version to control
- // driver behavior.
- //
- // TODO: We should replace this with use of `lld` eventually.
- args.push_back("-mlinker-version=705");
- break;
- default:
- break;
- }
- // Append our exact header search paths for the various parts of the C++
- // standard library headers as we don't build a single unified tree.
- for (const std::filesystem::path& runtime_path :
- {install_paths.libunwind_path(), install_paths.libcxx_path(),
- install_paths.libcxxabi_path()}) {
- args.push_back(
- llvm::formatv("-stdlib++-isystem{0}", runtime_path / "include").str());
- }
- }
- } // namespace Carbon
|