|
|
@@ -4,8 +4,16 @@
|
|
|
|
|
|
#include "toolchain/base/clang_invocation.h"
|
|
|
|
|
|
+#include <filesystem>
|
|
|
+#include <string>
|
|
|
+
|
|
|
#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 {
|
|
|
|
|
|
@@ -60,23 +68,36 @@ class ClangDriverDiagnosticConsumer : public clang::DiagnosticConsumer {
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
-static auto BuildClangInvocationImpl(
|
|
|
- Diagnostics::NoLocEmitter& emitter,
|
|
|
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
|
|
- llvm::ArrayRef<std::string> clang_path_and_args)
|
|
|
+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::SmallVector<const char*> driver_args(llvm::map_range(
|
|
|
- clang_path_and_args, [](const std::string& str) { return str.c_str(); }));
|
|
|
-
|
|
|
- // Add our include file name as the input file, and force it to be interpreted
|
|
|
- // as C++.
|
|
|
- driver_args.push_back("-x");
|
|
|
- driver_args.push_back("c++");
|
|
|
- driver_args.push_back(IncludesFileName);
|
|
|
+ llvm::OwningArrayRef<char> cstr_arg_storage;
|
|
|
+ llvm::SmallVector<const char*> cstr_args = BuildCStrArgs(
|
|
|
+ install_paths.clang_path().native(), args, extra_args, cstr_arg_storage);
|
|
|
|
|
|
// Build a diagnostics engine. Note that we don't have any diagnostic options
|
|
|
// yet; they're produced by running the driver.
|
|
|
@@ -88,19 +109,8 @@ static auto BuildClangInvocationImpl(
|
|
|
|
|
|
// Ask the driver to process the arguments and build a corresponding clang
|
|
|
// frontend invocation.
|
|
|
- return clang::createInvocation(driver_args,
|
|
|
- {.Diags = driver_diags, .VFS = fs});
|
|
|
-}
|
|
|
-
|
|
|
-auto BuildClangInvocation(Diagnostics::Consumer& consumer,
|
|
|
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
|
|
|
- llvm::ArrayRef<std::string> clang_path_and_args)
|
|
|
- -> std::unique_ptr<clang::CompilerInvocation> {
|
|
|
- Diagnostics::ErrorTrackingConsumer error_tracker(consumer);
|
|
|
- Diagnostics::NoLocEmitter emitter(&error_tracker);
|
|
|
-
|
|
|
- // Forward to the implementation to avoid exposing `import_cpp` outside check.
|
|
|
- auto invocation = BuildClangInvocationImpl(emitter, fs, clang_path_and_args);
|
|
|
+ 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()) {
|
|
|
@@ -110,4 +120,49 @@ auto BuildClangInvocation(Diagnostics::Consumer& consumer,
|
|
|
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",
|
|
|
+
|
|
|
+ // 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: Add flags for the installed runtimes using `install_paths`.
|
|
|
+}
|
|
|
+
|
|
|
} // namespace Carbon
|